starplot 0.13.0__py2.py3-none-any.whl → 0.15.0__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.
- starplot/__init__.py +5 -2
- starplot/base.py +311 -197
- starplot/cli.py +33 -0
- starplot/coordinates.py +6 -0
- starplot/data/__init__.py +6 -24
- starplot/data/bigsky.py +58 -40
- starplot/data/constellation_lines.py +827 -0
- starplot/data/constellation_stars.py +1501 -0
- starplot/data/constellations.py +600 -27
- starplot/data/db.py +17 -0
- starplot/data/dsos.py +24 -141
- starplot/data/stars.py +45 -24
- starplot/geod.py +0 -6
- starplot/geometry.py +181 -0
- starplot/horizon.py +302 -231
- starplot/map.py +100 -463
- starplot/mixins.py +75 -14
- starplot/models/__init__.py +1 -1
- starplot/models/base.py +18 -129
- starplot/models/constellation.py +55 -32
- starplot/models/dso.py +132 -67
- starplot/models/moon.py +57 -78
- starplot/models/planet.py +44 -69
- starplot/models/star.py +91 -60
- starplot/models/sun.py +32 -53
- starplot/optic.py +21 -18
- starplot/plotters/__init__.py +2 -0
- starplot/plotters/constellations.py +342 -0
- starplot/plotters/dsos.py +49 -68
- starplot/plotters/experimental.py +171 -0
- starplot/plotters/milkyway.py +39 -0
- starplot/plotters/stars.py +126 -122
- starplot/profile.py +16 -0
- starplot/settings.py +26 -0
- starplot/styles/__init__.py +2 -0
- starplot/styles/base.py +56 -34
- starplot/styles/ext/antique.yml +11 -9
- starplot/styles/ext/blue_dark.yml +8 -10
- starplot/styles/ext/blue_gold.yml +135 -0
- starplot/styles/ext/blue_light.yml +14 -12
- starplot/styles/ext/blue_medium.yml +23 -20
- starplot/styles/ext/cb_wong.yml +9 -7
- starplot/styles/ext/grayscale.yml +4 -3
- starplot/styles/ext/grayscale_dark.yml +7 -5
- starplot/styles/ext/map.yml +9 -6
- starplot/styles/ext/nord.yml +7 -7
- starplot/styles/ext/optic.yml +1 -1
- starplot/styles/extensions.py +1 -0
- starplot/utils.py +19 -0
- starplot/warnings.py +21 -0
- {starplot-0.13.0.dist-info → starplot-0.15.0.dist-info}/METADATA +19 -18
- starplot-0.15.0.dist-info/RECORD +97 -0
- starplot-0.15.0.dist-info/entry_points.txt +3 -0
- starplot/data/bayer.py +0 -3499
- starplot/data/flamsteed.py +0 -2682
- starplot/data/library/constellation_borders_inv.gpkg +0 -0
- starplot/data/library/constellation_lines_hips.json +0 -709
- starplot/data/library/constellation_lines_inv.gpkg +0 -0
- starplot/data/library/constellations.gpkg +0 -0
- starplot/data/library/constellations_hip.fab +0 -88
- starplot/data/library/milkyway.gpkg +0 -0
- starplot/data/library/milkyway_inv.gpkg +0 -0
- starplot/data/library/ongc.gpkg.zip +0 -0
- starplot/data/library/stars.bigsky.mag11.parquet +0 -0
- starplot/data/library/stars.hipparcos.parquet +0 -0
- starplot/data/messier.py +0 -111
- starplot/data/prep/__init__.py +0 -0
- starplot/data/prep/constellations.py +0 -108
- starplot/data/prep/dsos.py +0 -299
- starplot/data/prep/utils.py +0 -16
- starplot/models/geometry.py +0 -44
- starplot-0.13.0.dist-info/RECORD +0 -101
- {starplot-0.13.0.dist-info → starplot-0.15.0.dist-info}/LICENSE +0 -0
- {starplot-0.13.0.dist-info → starplot-0.15.0.dist-info}/WHEEL +0 -0
starplot/models/dso.py
CHANGED
|
@@ -1,23 +1,39 @@
|
|
|
1
|
-
from typing import Optional, Union
|
|
1
|
+
from typing import Optional, Union, Iterator
|
|
2
2
|
|
|
3
|
+
from ibis import _
|
|
3
4
|
from shapely.geometry import Polygon, MultiPolygon
|
|
4
5
|
|
|
5
|
-
from starplot.data.dsos import
|
|
6
|
+
from starplot.data.dsos import load
|
|
6
7
|
from starplot.mixins import CreateMapMixin, CreateOpticMixin
|
|
7
|
-
from starplot.models.base import SkyObject
|
|
8
|
-
from starplot.models.geometry import to_24h
|
|
9
|
-
from starplot import geod
|
|
8
|
+
from starplot.models.base import SkyObject
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
class
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
class DsoType:
|
|
12
|
+
"""
|
|
13
|
+
Type of deep sky object (DSOs), as designated in OpenNGC
|
|
14
|
+
"""
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
STAR = "*"
|
|
17
|
+
DOUBLE_STAR = "**"
|
|
18
|
+
ASSOCIATION_OF_STARS = "*Ass"
|
|
19
|
+
OPEN_CLUSTER = "OCl"
|
|
20
|
+
GLOBULAR_CLUSTER = "GCl"
|
|
21
|
+
GALAXY = "G"
|
|
22
|
+
GALAXY_PAIR = "GPair"
|
|
23
|
+
GALAXY_TRIPLET = "GTrpl"
|
|
24
|
+
GROUP_OF_GALAXIES = "GGroup"
|
|
25
|
+
NEBULA = "Neb"
|
|
26
|
+
PLANETARY_NEBULA = "PN"
|
|
27
|
+
EMISSION_NEBULA = "EmN"
|
|
28
|
+
STAR_CLUSTER_NEBULA = "Cl+N"
|
|
29
|
+
REFLECTION_NEBULA = "RfN"
|
|
30
|
+
DARK_NEBULA = "DrkN"
|
|
31
|
+
HII_IONIZED_REGION = "HII"
|
|
32
|
+
SUPERNOVA_REMNANT = "SNR"
|
|
33
|
+
NOVA_STAR = "Nova"
|
|
34
|
+
NONEXISTENT = "NonEx"
|
|
35
|
+
UNKNOWN = "Other"
|
|
36
|
+
DUPLICATE_RECORD = "Dup"
|
|
21
37
|
|
|
22
38
|
|
|
23
39
|
class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
|
|
@@ -26,12 +42,11 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
|
|
|
26
42
|
So, you can use any attributes of this model in your callables. Note that some may be null.
|
|
27
43
|
"""
|
|
28
44
|
|
|
29
|
-
_manager = DsoManager
|
|
30
|
-
|
|
31
45
|
name: str
|
|
32
46
|
"""Name of the DSO (as specified in OpenNGC)"""
|
|
33
47
|
|
|
34
48
|
type: DsoType
|
|
49
|
+
"""Type of DSO"""
|
|
35
50
|
|
|
36
51
|
magnitude: Optional[float] = None
|
|
37
52
|
"""Magnitude (if available)"""
|
|
@@ -64,7 +79,7 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
|
|
|
64
79
|
"""
|
|
65
80
|
|
|
66
81
|
geometry: Union[Polygon, MultiPolygon] = None
|
|
67
|
-
"""Shapely Polygon of the DSO's extent. Right ascension coordinates are in
|
|
82
|
+
"""Shapely Polygon of the DSO's extent. Right ascension coordinates are in degrees (0...360)."""
|
|
68
83
|
|
|
69
84
|
def __init__(
|
|
70
85
|
self,
|
|
@@ -81,8 +96,9 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
|
|
|
81
96
|
ngc: str = None,
|
|
82
97
|
ic: str = None,
|
|
83
98
|
geometry: Union[Polygon, MultiPolygon] = None,
|
|
99
|
+
constellation_id: str = None,
|
|
84
100
|
) -> None:
|
|
85
|
-
super().__init__(ra, dec)
|
|
101
|
+
super().__init__(ra, dec, constellation_id)
|
|
86
102
|
self.name = name
|
|
87
103
|
self.type = type
|
|
88
104
|
self.magnitude = magnitude
|
|
@@ -99,21 +115,47 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
|
|
|
99
115
|
return f"DSO(name={self.name}, magnitude={self.magnitude})"
|
|
100
116
|
|
|
101
117
|
@classmethod
|
|
102
|
-
def
|
|
118
|
+
def all(cls) -> Iterator["DSO"]:
|
|
119
|
+
df = load().to_pandas()
|
|
120
|
+
|
|
121
|
+
for d in df.itertuples():
|
|
122
|
+
yield from_tuple(d)
|
|
123
|
+
|
|
124
|
+
@classmethod
|
|
125
|
+
def get(cls, **kwargs) -> "DSO":
|
|
103
126
|
"""
|
|
104
127
|
Get a DSO, by matching its attributes.
|
|
105
128
|
|
|
106
|
-
Example:
|
|
129
|
+
Example:
|
|
130
|
+
|
|
131
|
+
d = DSO.get(m=13)
|
|
107
132
|
|
|
108
133
|
Args:
|
|
109
134
|
**kwargs: Attributes on the DSO you want to match
|
|
110
135
|
|
|
111
136
|
Raises: `ValueError` if more than one DSO is matched
|
|
112
137
|
"""
|
|
113
|
-
|
|
138
|
+
filters = []
|
|
139
|
+
|
|
140
|
+
for k, v in kwargs.items():
|
|
141
|
+
filters.append(getattr(_, k) == v)
|
|
142
|
+
|
|
143
|
+
df = load(filters=filters).to_pandas()
|
|
144
|
+
|
|
145
|
+
results = [from_tuple(d) for d in df.itertuples()]
|
|
146
|
+
|
|
147
|
+
if len(results) == 1:
|
|
148
|
+
return results[0]
|
|
149
|
+
|
|
150
|
+
if len(results) > 1:
|
|
151
|
+
raise ValueError(
|
|
152
|
+
"More than one match. Use find() instead or narrow your search."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return None
|
|
114
156
|
|
|
115
157
|
@classmethod
|
|
116
|
-
def find(where: list) -> list["DSO"]:
|
|
158
|
+
def find(cls, where: list) -> list["DSO"]:
|
|
117
159
|
"""
|
|
118
160
|
Find DSOs
|
|
119
161
|
|
|
@@ -124,60 +166,83 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
|
|
|
124
166
|
List of DSOs that match all `where` expressions
|
|
125
167
|
|
|
126
168
|
"""
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def create_ellipse(d):
|
|
131
|
-
maj_ax, min_ax, angle = d.maj_ax, d.min_ax, d.angle
|
|
132
|
-
|
|
133
|
-
if maj_ax is None:
|
|
134
|
-
return d.geometry
|
|
135
|
-
|
|
136
|
-
if angle is None:
|
|
137
|
-
angle = 0
|
|
138
|
-
|
|
139
|
-
maj_ax_degrees = (maj_ax / 60) / 2
|
|
140
|
-
|
|
141
|
-
if not min_ax:
|
|
142
|
-
min_ax_degrees = maj_ax_degrees
|
|
143
|
-
else:
|
|
144
|
-
min_ax_degrees = (min_ax / 60) / 2
|
|
145
|
-
|
|
146
|
-
points = geod.ellipse(
|
|
147
|
-
(d.ra_degrees / 15, d.dec_degrees),
|
|
148
|
-
min_ax_degrees * 2,
|
|
149
|
-
maj_ax_degrees * 2,
|
|
150
|
-
angle,
|
|
151
|
-
num_pts=100,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
# points = [geod.to_radec(p) for p in points]
|
|
155
|
-
points = [(round(ra, 4), round(dec, 4)) for ra, dec in points]
|
|
156
|
-
return Polygon(points)
|
|
169
|
+
df = load(filters=where).to_pandas()
|
|
170
|
+
return [from_tuple(d) for d in df.itertuples()]
|
|
157
171
|
|
|
158
172
|
|
|
159
173
|
def from_tuple(d: tuple) -> DSO:
|
|
160
|
-
|
|
161
|
-
magnitude = float(magnitude) if magnitude else None
|
|
162
|
-
geometry = d.geometry
|
|
163
|
-
|
|
164
|
-
if str(geometry.geom_type) not in ["Polygon", "MultiPolygon"]:
|
|
165
|
-
geometry = create_ellipse(d)
|
|
166
|
-
|
|
167
|
-
geometry = to_24h(geometry)
|
|
168
|
-
|
|
169
|
-
return DSO(
|
|
174
|
+
dso = DSO(
|
|
170
175
|
name=d.name,
|
|
171
|
-
ra=d.
|
|
172
|
-
dec=d.
|
|
173
|
-
type=
|
|
176
|
+
ra=d.ra,
|
|
177
|
+
dec=d.dec,
|
|
178
|
+
type=d.type,
|
|
174
179
|
maj_ax=d.maj_ax,
|
|
175
180
|
min_ax=d.min_ax,
|
|
176
181
|
angle=d.angle,
|
|
177
|
-
magnitude=magnitude,
|
|
178
|
-
size=d.
|
|
182
|
+
magnitude=d.magnitude,
|
|
183
|
+
size=d.size,
|
|
179
184
|
m=d.m,
|
|
180
185
|
ngc=d.ngc,
|
|
181
186
|
ic=d.ic,
|
|
182
|
-
geometry=geometry,
|
|
187
|
+
geometry=d.geometry,
|
|
188
|
+
constellation_id=d.constellation_id,
|
|
183
189
|
)
|
|
190
|
+
dso._row_id = getattr(d, "rowid", None)
|
|
191
|
+
return dso
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
ONGC_TYPE = {
|
|
195
|
+
# Star Clusters ----------
|
|
196
|
+
DsoType.OPEN_CLUSTER: "OCl",
|
|
197
|
+
DsoType.GLOBULAR_CLUSTER: "GCl",
|
|
198
|
+
# Galaxies ----------
|
|
199
|
+
DsoType.GALAXY: "G",
|
|
200
|
+
DsoType.GALAXY_PAIR: "GPair",
|
|
201
|
+
DsoType.GALAXY_TRIPLET: "GTrpl",
|
|
202
|
+
DsoType.GROUP_OF_GALAXIES: "GGroup",
|
|
203
|
+
# Nebulas ----------
|
|
204
|
+
DsoType.NEBULA: "Neb",
|
|
205
|
+
DsoType.PLANETARY_NEBULA: "PN",
|
|
206
|
+
DsoType.EMISSION_NEBULA: "EmN",
|
|
207
|
+
DsoType.STAR_CLUSTER_NEBULA: "Cl+N",
|
|
208
|
+
DsoType.REFLECTION_NEBULA: "RfN",
|
|
209
|
+
# Stars ----------
|
|
210
|
+
DsoType.STAR: "*",
|
|
211
|
+
DsoType.DOUBLE_STAR: "**",
|
|
212
|
+
DsoType.ASSOCIATION_OF_STARS: "*Ass",
|
|
213
|
+
# Others
|
|
214
|
+
DsoType.HII_IONIZED_REGION: "HII",
|
|
215
|
+
DsoType.DARK_NEBULA: "DrkN",
|
|
216
|
+
DsoType.SUPERNOVA_REMNANT: "SNR",
|
|
217
|
+
DsoType.NOVA_STAR: "Nova",
|
|
218
|
+
DsoType.NONEXISTENT: "NonEx",
|
|
219
|
+
DsoType.UNKNOWN: "Other",
|
|
220
|
+
DsoType.DUPLICATE_RECORD: "Dup",
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
ONGC_TYPE_MAP = {v: k for k, v in ONGC_TYPE.items()}
|
|
224
|
+
|
|
225
|
+
DSO_LEGEND_LABELS = {
|
|
226
|
+
# Galaxies ----------
|
|
227
|
+
DsoType.GALAXY: "Galaxy",
|
|
228
|
+
DsoType.GALAXY_PAIR: "Galaxy",
|
|
229
|
+
DsoType.GALAXY_TRIPLET: "Galaxy",
|
|
230
|
+
DsoType.GROUP_OF_GALAXIES: "Galaxy",
|
|
231
|
+
# Nebulas ----------
|
|
232
|
+
DsoType.NEBULA: "Nebula",
|
|
233
|
+
DsoType.PLANETARY_NEBULA: "Nebula",
|
|
234
|
+
DsoType.EMISSION_NEBULA: "Nebula",
|
|
235
|
+
DsoType.STAR_CLUSTER_NEBULA: "Nebula",
|
|
236
|
+
DsoType.REFLECTION_NEBULA: "Nebula",
|
|
237
|
+
# Star Clusters ----------
|
|
238
|
+
DsoType.OPEN_CLUSTER: "Open Cluster",
|
|
239
|
+
DsoType.GLOBULAR_CLUSTER: "Globular Cluster",
|
|
240
|
+
# Stars ----------
|
|
241
|
+
DsoType.DOUBLE_STAR: "Double Star",
|
|
242
|
+
DsoType.ASSOCIATION_OF_STARS: "Association of stars",
|
|
243
|
+
DsoType.NOVA_STAR: "Nova Star",
|
|
244
|
+
# Others
|
|
245
|
+
DsoType.HII_IONIZED_REGION: "HII Ionized Region",
|
|
246
|
+
DsoType.DARK_NEBULA: "Dark Nebula",
|
|
247
|
+
DsoType.SUPERNOVA_REMNANT: "Supernova Remnant",
|
|
248
|
+
}
|
starplot/models/moon.py
CHANGED
|
@@ -7,8 +7,8 @@ from skyfield import almanac
|
|
|
7
7
|
from shapely import Polygon
|
|
8
8
|
|
|
9
9
|
from starplot.data import load
|
|
10
|
-
from starplot.models.base import SkyObject
|
|
11
|
-
from starplot.
|
|
10
|
+
from starplot.models.base import SkyObject
|
|
11
|
+
from starplot.geometry import circle
|
|
12
12
|
from starplot.utils import dt_or_now
|
|
13
13
|
|
|
14
14
|
|
|
@@ -25,14 +25,50 @@ class MoonPhase(str, Enum):
|
|
|
25
25
|
WANING_CRESCENT = "Waning Crescent"
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class
|
|
29
|
-
|
|
30
|
-
def all(cls):
|
|
31
|
-
raise NotImplementedError
|
|
28
|
+
class Moon(SkyObject):
|
|
29
|
+
"""Moon model. Only used for Earth's moon right now, but will potentially represent other planets' moons in future versions."""
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
name: str = "Moon"
|
|
32
|
+
"""Name of the moon"""
|
|
33
|
+
|
|
34
|
+
dt: datetime
|
|
35
|
+
"""Date/time of moon's position"""
|
|
36
|
+
|
|
37
|
+
apparent_size: float
|
|
38
|
+
"""Apparent size (degrees)"""
|
|
39
|
+
|
|
40
|
+
phase_angle: float
|
|
41
|
+
"""Angle of the moon from the Sun (degrees)"""
|
|
42
|
+
|
|
43
|
+
phase_description: str
|
|
44
|
+
"""Description of the moon's phase. The Moon will be considered New/Full/Quarter if it's within 12 hours of that precise phase."""
|
|
45
|
+
|
|
46
|
+
illumination: float
|
|
47
|
+
"""Percent of illumination (0...1)"""
|
|
48
|
+
|
|
49
|
+
geometry: Polygon = None
|
|
50
|
+
"""Shapely Polygon of the moon's extent. Right ascension coordinates are in degrees (0...360)."""
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
ra: float,
|
|
55
|
+
dec: float,
|
|
56
|
+
name: str,
|
|
57
|
+
dt: datetime,
|
|
58
|
+
apparent_size: float,
|
|
59
|
+
phase_angle: float,
|
|
60
|
+
phase_description: str,
|
|
61
|
+
illumination: str,
|
|
62
|
+
geometry: Polygon = None,
|
|
63
|
+
) -> None:
|
|
64
|
+
super().__init__(ra, dec)
|
|
65
|
+
self.name = name
|
|
66
|
+
self.dt = dt
|
|
67
|
+
self.apparent_size = apparent_size
|
|
68
|
+
self.phase_angle = phase_angle
|
|
69
|
+
self.phase_description = phase_description
|
|
70
|
+
self.illumination = illumination
|
|
71
|
+
self.geometry = geometry
|
|
36
72
|
|
|
37
73
|
@classmethod
|
|
38
74
|
def get(
|
|
@@ -41,7 +77,16 @@ class MoonManager(SkyObjectManager):
|
|
|
41
77
|
lat: float = None,
|
|
42
78
|
lon: float = None,
|
|
43
79
|
ephemeris: str = "de421_2001.bsp",
|
|
44
|
-
):
|
|
80
|
+
) -> "Moon":
|
|
81
|
+
"""
|
|
82
|
+
Get the Moon for a specific date/time and observing location.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
dt: Datetime you want the moon for (must be timezone aware!). _Defaults to current UTC time_.
|
|
86
|
+
lat: Latitude of observing location. If you set this (and longitude), then the Moon's _apparent_ RA/DEC will be calculated.
|
|
87
|
+
lon: Longitude of observing location
|
|
88
|
+
ephemeris: Ephemeris to use for calculating moon positions (see [Skyfield's documentation](https://rhodesmill.org/skyfield/planets.html) for details)
|
|
89
|
+
"""
|
|
45
90
|
RADIUS_KM = 1_740
|
|
46
91
|
|
|
47
92
|
dt = dt_or_now(dt)
|
|
@@ -106,7 +151,7 @@ class MoonManager(SkyObjectManager):
|
|
|
106
151
|
phase = MoonPhase.WANING_CRESCENT
|
|
107
152
|
|
|
108
153
|
return Moon(
|
|
109
|
-
ra=ra.hours,
|
|
154
|
+
ra=ra.hours * 15,
|
|
110
155
|
dec=dec.degrees,
|
|
111
156
|
name="Moon",
|
|
112
157
|
dt=dt,
|
|
@@ -114,71 +159,5 @@ class MoonManager(SkyObjectManager):
|
|
|
114
159
|
phase_angle=phase_angle,
|
|
115
160
|
phase_description=phase.value,
|
|
116
161
|
illumination=illumination,
|
|
117
|
-
geometry=circle((ra.hours, dec.degrees), apparent_diameter_degrees),
|
|
162
|
+
geometry=circle((ra.hours * 15, dec.degrees), apparent_diameter_degrees),
|
|
118
163
|
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
class Moon(SkyObject):
|
|
122
|
-
"""Moon model. Only used for Earth's moon right now, but will potentially represent other planets' moons in future versions."""
|
|
123
|
-
|
|
124
|
-
_manager = MoonManager
|
|
125
|
-
|
|
126
|
-
name: str = "Moon"
|
|
127
|
-
"""Name of the moon"""
|
|
128
|
-
|
|
129
|
-
dt: datetime
|
|
130
|
-
"""Date/time of moon's position"""
|
|
131
|
-
|
|
132
|
-
apparent_size: float
|
|
133
|
-
"""Apparent size (degrees)"""
|
|
134
|
-
|
|
135
|
-
phase_angle: float
|
|
136
|
-
"""Angle of the moon from the Sun (degrees)"""
|
|
137
|
-
|
|
138
|
-
phase_description: str
|
|
139
|
-
"""Description of the moon's phase. The Moon will be considered New/Full/Quarter if it's within 12 hours of that precise phase."""
|
|
140
|
-
|
|
141
|
-
illumination: float
|
|
142
|
-
"""Percent of illumination (0...1)"""
|
|
143
|
-
|
|
144
|
-
geometry: Polygon = None
|
|
145
|
-
"""Shapely Polygon of the moon's extent. Right ascension coordinates are in 24H format."""
|
|
146
|
-
|
|
147
|
-
def __init__(
|
|
148
|
-
self,
|
|
149
|
-
ra: float,
|
|
150
|
-
dec: float,
|
|
151
|
-
name: str,
|
|
152
|
-
dt: datetime,
|
|
153
|
-
apparent_size: float,
|
|
154
|
-
phase_angle: float,
|
|
155
|
-
phase_description: str,
|
|
156
|
-
illumination: str,
|
|
157
|
-
geometry: Polygon = None,
|
|
158
|
-
) -> None:
|
|
159
|
-
super().__init__(ra, dec)
|
|
160
|
-
self.name = name
|
|
161
|
-
self.dt = dt
|
|
162
|
-
self.apparent_size = apparent_size
|
|
163
|
-
self.phase_angle = phase_angle
|
|
164
|
-
self.phase_description = phase_description
|
|
165
|
-
self.illumination = illumination
|
|
166
|
-
self.geometry = geometry
|
|
167
|
-
|
|
168
|
-
@classmethod
|
|
169
|
-
def get(
|
|
170
|
-
dt: datetime = None,
|
|
171
|
-
lat: float = None,
|
|
172
|
-
lon: float = None,
|
|
173
|
-
ephemeris: str = "de421_2001.bsp",
|
|
174
|
-
) -> "Moon":
|
|
175
|
-
"""
|
|
176
|
-
Get the Moon for a specific date/time and observing location.
|
|
177
|
-
|
|
178
|
-
Args:
|
|
179
|
-
dt: Datetime you want the moon for (must be timezone aware!). _Defaults to current UTC time_.
|
|
180
|
-
lat: Latitude of observing location. If you set this (and longitude), then the Moon's _apparent_ RA/DEC will be calculated.
|
|
181
|
-
lon: Longitude of observing location
|
|
182
|
-
ephemeris: Ephemeris to use for calculating moon positions (see [Skyfield's documentation](https://rhodesmill.org/skyfield/planets.html) for details)
|
|
183
|
-
"""
|
|
184
|
-
pass
|
starplot/models/planet.py
CHANGED
|
@@ -8,8 +8,8 @@ from skyfield.api import Angle, wgs84
|
|
|
8
8
|
from shapely import Polygon
|
|
9
9
|
|
|
10
10
|
from starplot.data import load
|
|
11
|
-
from starplot.models.base import SkyObject
|
|
12
|
-
from starplot.
|
|
11
|
+
from starplot.models.base import SkyObject
|
|
12
|
+
from starplot.geometry import circle
|
|
13
13
|
from starplot.utils import dt_or_now
|
|
14
14
|
|
|
15
15
|
|
|
@@ -48,73 +48,9 @@ Retrieved on 18-APR-2024
|
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
class PlanetManager(SkyObjectManager):
|
|
52
|
-
@classmethod
|
|
53
|
-
def all(
|
|
54
|
-
cls,
|
|
55
|
-
dt: datetime = None,
|
|
56
|
-
lat: float = None,
|
|
57
|
-
lon: float = None,
|
|
58
|
-
ephemeris: str = "de421_2001.bsp",
|
|
59
|
-
):
|
|
60
|
-
dt = dt_or_now(dt)
|
|
61
|
-
ephemeris = load(ephemeris)
|
|
62
|
-
timescale = load.timescale().from_datetime(dt)
|
|
63
|
-
earth = ephemeris["earth"]
|
|
64
|
-
|
|
65
|
-
for p in PlanetName:
|
|
66
|
-
planet = ephemeris[f"{p.value} barycenter"]
|
|
67
|
-
|
|
68
|
-
if lat is not None and lon is not None:
|
|
69
|
-
position = earth + wgs84.latlon(lat, lon)
|
|
70
|
-
astrometric = position.at(timescale).observe(planet)
|
|
71
|
-
apparent = astrometric.apparent()
|
|
72
|
-
ra, dec, distance = apparent.radec()
|
|
73
|
-
else:
|
|
74
|
-
astrometric = earth.at(timescale).observe(planet)
|
|
75
|
-
ra, dec, distance = astrometric.radec()
|
|
76
|
-
|
|
77
|
-
# angular diameter:
|
|
78
|
-
# https://rhodesmill.org/skyfield/examples.html#what-is-the-angular-diameter-of-a-planet-given-its-radius
|
|
79
|
-
apparent_diameter_degrees = Angle(
|
|
80
|
-
radians=np.arcsin(PLANET_RADIUS_KM[p] / distance.km) * 2.0
|
|
81
|
-
).degrees
|
|
82
|
-
|
|
83
|
-
yield Planet(
|
|
84
|
-
ra=ra.hours,
|
|
85
|
-
dec=dec.degrees,
|
|
86
|
-
name=p,
|
|
87
|
-
dt=dt,
|
|
88
|
-
apparent_size=apparent_diameter_degrees,
|
|
89
|
-
geometry=circle((ra.hours, dec.degrees), apparent_diameter_degrees),
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
@classmethod
|
|
93
|
-
def find(cls):
|
|
94
|
-
raise NotImplementedError
|
|
95
|
-
|
|
96
|
-
@classmethod
|
|
97
|
-
def get(
|
|
98
|
-
cls,
|
|
99
|
-
name: str,
|
|
100
|
-
dt: datetime = None,
|
|
101
|
-
lat: float = None,
|
|
102
|
-
lon: float = None,
|
|
103
|
-
ephemeris: str = "de421_2001.bsp",
|
|
104
|
-
):
|
|
105
|
-
dt = dt_or_now(dt)
|
|
106
|
-
for p in cls.all(dt, lat, lon, ephemeris):
|
|
107
|
-
if p.name.lower() == name.lower():
|
|
108
|
-
return p
|
|
109
|
-
|
|
110
|
-
return None
|
|
111
|
-
|
|
112
|
-
|
|
113
51
|
class Planet(SkyObject):
|
|
114
52
|
"""Planet model."""
|
|
115
53
|
|
|
116
|
-
_manager = PlanetManager
|
|
117
|
-
|
|
118
54
|
name: str
|
|
119
55
|
"""
|
|
120
56
|
Name of the planet:
|
|
@@ -137,7 +73,7 @@ class Planet(SkyObject):
|
|
|
137
73
|
"""Apparent size (degrees)"""
|
|
138
74
|
|
|
139
75
|
geometry: Polygon = None
|
|
140
|
-
"""Shapely Polygon of the planet's extent. Right ascension coordinates are in
|
|
76
|
+
"""Shapely Polygon of the planet's extent. Right ascension coordinates are in degrees (0...360)."""
|
|
141
77
|
|
|
142
78
|
def __init__(
|
|
143
79
|
self,
|
|
@@ -156,6 +92,7 @@ class Planet(SkyObject):
|
|
|
156
92
|
|
|
157
93
|
@classmethod
|
|
158
94
|
def all(
|
|
95
|
+
cls,
|
|
159
96
|
dt: datetime = None,
|
|
160
97
|
lat: float = None,
|
|
161
98
|
lon: float = None,
|
|
@@ -170,10 +107,43 @@ class Planet(SkyObject):
|
|
|
170
107
|
lon: Longitude of observing location
|
|
171
108
|
ephemeris: Ephemeris to use for calculating planet positions (see [Skyfield's documentation](https://rhodesmill.org/skyfield/planets.html) for details)
|
|
172
109
|
"""
|
|
173
|
-
|
|
110
|
+
dt = dt_or_now(dt)
|
|
111
|
+
ephemeris = load(ephemeris)
|
|
112
|
+
timescale = load.timescale().from_datetime(dt)
|
|
113
|
+
earth = ephemeris["earth"]
|
|
114
|
+
|
|
115
|
+
for p in PlanetName:
|
|
116
|
+
planet = ephemeris[f"{p.value} barycenter"]
|
|
117
|
+
|
|
118
|
+
if lat is not None and lon is not None:
|
|
119
|
+
position = earth + wgs84.latlon(lat, lon)
|
|
120
|
+
astrometric = position.at(timescale).observe(planet)
|
|
121
|
+
apparent = astrometric.apparent()
|
|
122
|
+
ra, dec, distance = apparent.radec()
|
|
123
|
+
else:
|
|
124
|
+
astrometric = earth.at(timescale).observe(planet)
|
|
125
|
+
ra, dec, distance = astrometric.radec()
|
|
126
|
+
|
|
127
|
+
# angular diameter:
|
|
128
|
+
# https://rhodesmill.org/skyfield/examples.html#what-is-the-angular-diameter-of-a-planet-given-its-radius
|
|
129
|
+
apparent_diameter_degrees = Angle(
|
|
130
|
+
radians=np.arcsin(PLANET_RADIUS_KM[p] / distance.km) * 2.0
|
|
131
|
+
).degrees
|
|
132
|
+
|
|
133
|
+
yield Planet(
|
|
134
|
+
ra=ra.hours * 15,
|
|
135
|
+
dec=dec.degrees,
|
|
136
|
+
name=p,
|
|
137
|
+
dt=dt,
|
|
138
|
+
apparent_size=apparent_diameter_degrees,
|
|
139
|
+
geometry=circle(
|
|
140
|
+
(ra.hours * 15, dec.degrees), apparent_diameter_degrees
|
|
141
|
+
),
|
|
142
|
+
)
|
|
174
143
|
|
|
175
144
|
@classmethod
|
|
176
145
|
def get(
|
|
146
|
+
cls,
|
|
177
147
|
name: str,
|
|
178
148
|
dt: datetime = None,
|
|
179
149
|
lat: float = None,
|
|
@@ -190,4 +160,9 @@ class Planet(SkyObject):
|
|
|
190
160
|
lon: Longitude of observing location
|
|
191
161
|
ephemeris: Ephemeris to use for calculating planet positions (see [Skyfield's documentation](https://rhodesmill.org/skyfield/planets.html) for details)
|
|
192
162
|
"""
|
|
193
|
-
|
|
163
|
+
dt = dt_or_now(dt)
|
|
164
|
+
for p in cls.all(dt, lat, lon, ephemeris):
|
|
165
|
+
if p.name.lower() == name.lower():
|
|
166
|
+
return p
|
|
167
|
+
|
|
168
|
+
return None
|