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
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
import rtree
|
|
4
|
+
from shapely import (
|
|
5
|
+
MultiPoint,
|
|
6
|
+
)
|
|
7
|
+
from matplotlib.collections import LineCollection
|
|
8
|
+
from ibis import _
|
|
9
|
+
|
|
10
|
+
from starplot.coordinates import CoordinateSystem
|
|
11
|
+
from starplot.data import constellations as condata, constellation_lines as conlines
|
|
12
|
+
from starplot.data.stars import load as load_stars, StarCatalog
|
|
13
|
+
from starplot.data.constellations import (
|
|
14
|
+
CONSTELLATIONS_FULL_NAMES,
|
|
15
|
+
CONSTELLATION_HIP_IDS,
|
|
16
|
+
)
|
|
17
|
+
from starplot.data.constellation_stars import CONSTELLATION_HIPS
|
|
18
|
+
from starplot.models.constellation import from_tuple as constellation_from_tuple
|
|
19
|
+
from starplot.projections import Projection
|
|
20
|
+
from starplot.profile import profile
|
|
21
|
+
from starplot.styles import PathStyle, LineStyle, LabelStyle
|
|
22
|
+
from starplot.styles.helpers import use_style
|
|
23
|
+
from starplot.utils import points_on_line
|
|
24
|
+
from starplot.geometry import is_wrapped_polygon
|
|
25
|
+
|
|
26
|
+
DEFAULT_AUTO_ADJUST_SETTINGS = {
|
|
27
|
+
"avoid_constellation_lines": False,
|
|
28
|
+
"point_generation_max_iterations": 10,
|
|
29
|
+
"distance_step_size": 2,
|
|
30
|
+
"max_distance": 3_000,
|
|
31
|
+
"label_padding": 6,
|
|
32
|
+
"buffer": 0.3,
|
|
33
|
+
"seed": None,
|
|
34
|
+
}
|
|
35
|
+
"""Default settings for auto-adjusting constellation labels"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ConstellationPlotterMixin:
|
|
39
|
+
def inbounds_temp(self, x, y):
|
|
40
|
+
data_x, data_y = self._proj.transform_point(x, y, self._geodetic)
|
|
41
|
+
display_x, display_y = self.ax.transData.transform((data_x, data_y))
|
|
42
|
+
return display_x > 0 and display_y > 0
|
|
43
|
+
|
|
44
|
+
@profile
|
|
45
|
+
def _prepare_constellation_stars(self) -> dict[int, tuple[float, float]]:
|
|
46
|
+
"""
|
|
47
|
+
Returns dictionary of stars and their position:
|
|
48
|
+
|
|
49
|
+
{hip: (x,y)}
|
|
50
|
+
|
|
51
|
+
Where (x, y) is the plotted coordinate system (RA/DEC or AZ/ALT)
|
|
52
|
+
"""
|
|
53
|
+
results = load_stars(
|
|
54
|
+
catalog=StarCatalog.BIG_SKY_MAG11,
|
|
55
|
+
filters=[_.hip.isin(CONSTELLATION_HIPS)],
|
|
56
|
+
)
|
|
57
|
+
df = results.to_pandas()
|
|
58
|
+
df = self._prepare_star_coords(df, limit_by_altaz=False)
|
|
59
|
+
|
|
60
|
+
return {star.hip: (star.x, star.y) for star in df.itertuples()}
|
|
61
|
+
|
|
62
|
+
@profile
|
|
63
|
+
@use_style(LineStyle, "constellation_lines")
|
|
64
|
+
def constellations(
|
|
65
|
+
self,
|
|
66
|
+
style: LineStyle = None,
|
|
67
|
+
where: list = None,
|
|
68
|
+
):
|
|
69
|
+
"""Plots the constellation lines **only**. To plot constellation borders and/or labels, see separate functions for them.
|
|
70
|
+
|
|
71
|
+
**Important:** If you're plotting the constellation lines, then it's good to plot them _first_, because Starplot will use the constellation lines to determine where to place labels that are plotted afterwards (labels will look better if they're not crossing a constellation line).
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
style: Styling of the constellations. If None, then the plot's style (specified when creating the plot) will be used
|
|
75
|
+
where: A list of expressions that determine which constellations to plot. See [Selecting Objects](/reference-selecting-objects/) for details.
|
|
76
|
+
"""
|
|
77
|
+
self.logger.debug("Plotting constellation lines...")
|
|
78
|
+
|
|
79
|
+
where = where or []
|
|
80
|
+
ctr = 0
|
|
81
|
+
|
|
82
|
+
extent = self._extent_mask()
|
|
83
|
+
results = condata.load(extent=extent, filters=where)
|
|
84
|
+
constellations_df = results.to_pandas()
|
|
85
|
+
|
|
86
|
+
if constellations_df.empty:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
if getattr(self, "projection", None) in [
|
|
90
|
+
Projection.MERCATOR,
|
|
91
|
+
Projection.MILLER,
|
|
92
|
+
]:
|
|
93
|
+
transform = self._plate_carree
|
|
94
|
+
else:
|
|
95
|
+
transform = self._geodetic
|
|
96
|
+
|
|
97
|
+
style_kwargs = style.matplot_kwargs(self.scale)
|
|
98
|
+
constellation_points_to_index = []
|
|
99
|
+
lines = []
|
|
100
|
+
constars = self._prepare_constellation_stars()
|
|
101
|
+
|
|
102
|
+
for c in constellations_df.itertuples():
|
|
103
|
+
hiplines = conlines.hips[c.iau_id]
|
|
104
|
+
inbounds = False
|
|
105
|
+
|
|
106
|
+
for s1_hip, s2_hip in hiplines:
|
|
107
|
+
if not constars.get(s2_hip):
|
|
108
|
+
continue
|
|
109
|
+
s1_ra, s1_dec = constars.get(s1_hip)
|
|
110
|
+
s2_ra, s2_dec = constars.get(s2_hip)
|
|
111
|
+
|
|
112
|
+
if s1_ra - s2_ra > 60:
|
|
113
|
+
s2_ra += 360
|
|
114
|
+
elif s2_ra - s1_ra > 60:
|
|
115
|
+
s1_ra += 360
|
|
116
|
+
|
|
117
|
+
x1, x2 = s1_ra, s2_ra
|
|
118
|
+
y1, y2 = s1_dec, s2_dec
|
|
119
|
+
if not inbounds and (
|
|
120
|
+
self._in_bounds_xy(x1, y1) or self._in_bounds_xy(x2, y2)
|
|
121
|
+
):
|
|
122
|
+
inbounds = True
|
|
123
|
+
elif not inbounds:
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
if self._coordinate_system == CoordinateSystem.RA_DEC:
|
|
127
|
+
x1 *= -1
|
|
128
|
+
x2 *= -1
|
|
129
|
+
|
|
130
|
+
lines.append([(x1, y1), (x2, y2)])
|
|
131
|
+
|
|
132
|
+
start = self._proj.transform_point(x1, y1, self._geodetic)
|
|
133
|
+
end = self._proj.transform_point(x2, y2, self._geodetic)
|
|
134
|
+
radius = style.width or 1
|
|
135
|
+
|
|
136
|
+
if any([np.isnan(n) for n in start + end]):
|
|
137
|
+
continue
|
|
138
|
+
|
|
139
|
+
for x, y in points_on_line(start, end, 25):
|
|
140
|
+
display_x, display_y = self.ax.transData.transform((x, y))
|
|
141
|
+
if display_x < 0 or display_y < 0:
|
|
142
|
+
continue
|
|
143
|
+
constellation_points_to_index.append(
|
|
144
|
+
(
|
|
145
|
+
ctr,
|
|
146
|
+
(
|
|
147
|
+
display_x - radius,
|
|
148
|
+
display_y - radius,
|
|
149
|
+
display_x + radius,
|
|
150
|
+
display_y + radius,
|
|
151
|
+
),
|
|
152
|
+
None,
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
ctr += 1
|
|
156
|
+
|
|
157
|
+
if inbounds:
|
|
158
|
+
obj = constellation_from_tuple(c)
|
|
159
|
+
self._objects.constellations.append(obj)
|
|
160
|
+
|
|
161
|
+
style_kwargs = style.matplot_line_collection_kwargs(self.scale)
|
|
162
|
+
|
|
163
|
+
line_collection = LineCollection(
|
|
164
|
+
lines,
|
|
165
|
+
**style_kwargs,
|
|
166
|
+
transform=transform,
|
|
167
|
+
clip_on=True,
|
|
168
|
+
clip_path=self._background_clip_path,
|
|
169
|
+
gid="constellations-line",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
self.ax.add_collection(line_collection)
|
|
173
|
+
|
|
174
|
+
if self._constellations_rtree.get_size() == 0:
|
|
175
|
+
self._constellations_rtree = rtree.index.Index(
|
|
176
|
+
constellation_points_to_index
|
|
177
|
+
)
|
|
178
|
+
else:
|
|
179
|
+
for bbox in constellation_points_to_index:
|
|
180
|
+
self._constellations_rtree.insert(
|
|
181
|
+
0,
|
|
182
|
+
bbox,
|
|
183
|
+
None,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
def _plot_constellation_labels(
|
|
187
|
+
self,
|
|
188
|
+
style: PathStyle = None,
|
|
189
|
+
labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
|
|
190
|
+
):
|
|
191
|
+
"""
|
|
192
|
+
TODO:
|
|
193
|
+
1. plot label, if removed then get size in display coords
|
|
194
|
+
2. generate random points in polygon, convert to display coords, test for intersections
|
|
195
|
+
3. plot best score
|
|
196
|
+
|
|
197
|
+
problem = constellations usually plotted first, so wont have star data (or could use stars from constellations only?)
|
|
198
|
+
|
|
199
|
+
constellation names CAN cross lines but not stars
|
|
200
|
+
|
|
201
|
+
"""
|
|
202
|
+
style = style or self.style.constellation.label
|
|
203
|
+
self._constellation_labels = []
|
|
204
|
+
|
|
205
|
+
for con in condata.iterator():
|
|
206
|
+
_, ra, dec = condata.get(con)
|
|
207
|
+
text = labels.get(con.lower())
|
|
208
|
+
label = self.text(
|
|
209
|
+
text,
|
|
210
|
+
ra,
|
|
211
|
+
dec,
|
|
212
|
+
style,
|
|
213
|
+
hide_on_collision=False,
|
|
214
|
+
# hide_on_collision=self.hide_colliding_labels,
|
|
215
|
+
gid="constellations-label-name",
|
|
216
|
+
)
|
|
217
|
+
if label is not None:
|
|
218
|
+
self._constellation_labels.append(label)
|
|
219
|
+
|
|
220
|
+
@profile
|
|
221
|
+
@use_style(LineStyle, "constellation_borders")
|
|
222
|
+
def constellation_borders(self, style: LineStyle = None):
|
|
223
|
+
"""Plots the constellation borders
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
style: Styling of the constellation borders. If None, then the plot's style (specified when creating the plot) will be used
|
|
227
|
+
"""
|
|
228
|
+
extent = self._extent_mask()
|
|
229
|
+
results = condata.load_borders(extent=extent)
|
|
230
|
+
borders_df = results.to_pandas()
|
|
231
|
+
|
|
232
|
+
if borders_df.empty:
|
|
233
|
+
return
|
|
234
|
+
|
|
235
|
+
border_lines = []
|
|
236
|
+
geometries = [line.geometry for line in borders_df.itertuples()]
|
|
237
|
+
|
|
238
|
+
for ls in geometries:
|
|
239
|
+
if ls.length < 80:
|
|
240
|
+
ls = ls.segmentize(1)
|
|
241
|
+
|
|
242
|
+
xy = [c for c in ls.coords]
|
|
243
|
+
|
|
244
|
+
if self._coordinate_system == CoordinateSystem.RA_DEC:
|
|
245
|
+
border_lines.append(xy)
|
|
246
|
+
|
|
247
|
+
elif self._coordinate_system == CoordinateSystem.AZ_ALT:
|
|
248
|
+
coords = [self._prepare_coords(*p) for p in xy]
|
|
249
|
+
border_lines.append(coords)
|
|
250
|
+
|
|
251
|
+
else:
|
|
252
|
+
raise ValueError("Unrecognized coordinate system")
|
|
253
|
+
|
|
254
|
+
line_collection = LineCollection(
|
|
255
|
+
border_lines,
|
|
256
|
+
**style.matplot_line_collection_kwargs(self.scale),
|
|
257
|
+
transform=self._crs,
|
|
258
|
+
clip_on=True,
|
|
259
|
+
clip_path=self._background_clip_path,
|
|
260
|
+
gid="constellations-border",
|
|
261
|
+
)
|
|
262
|
+
self.ax.add_collection(line_collection)
|
|
263
|
+
|
|
264
|
+
def _constellation_labels_auto(self, style, labels, settings):
|
|
265
|
+
for constellation in self.objects.constellations:
|
|
266
|
+
constellation_line_stars = [
|
|
267
|
+
s
|
|
268
|
+
for s in self.objects.stars
|
|
269
|
+
if s.hip in CONSTELLATION_HIP_IDS[constellation.iau_id]
|
|
270
|
+
]
|
|
271
|
+
if not constellation_line_stars:
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
if is_wrapped_polygon(constellation.boundary):
|
|
275
|
+
starpoints = []
|
|
276
|
+
ra, dec = zip(*[(s.ra, s.dec) for s in constellation_line_stars])
|
|
277
|
+
new_ra = [r - 360 if r > 300 else r for r in ra]
|
|
278
|
+
starpoints = list(zip(new_ra, dec))
|
|
279
|
+
|
|
280
|
+
else:
|
|
281
|
+
ra, dec = zip(*[(s.ra, s.dec) for s in constellation_line_stars])
|
|
282
|
+
starpoints = list(zip(ra, dec))
|
|
283
|
+
|
|
284
|
+
points_line = MultiPoint(starpoints)
|
|
285
|
+
centroid = points_line.centroid
|
|
286
|
+
text = labels.get(constellation.iau_id)
|
|
287
|
+
|
|
288
|
+
self.text(
|
|
289
|
+
text,
|
|
290
|
+
centroid.x,
|
|
291
|
+
centroid.y,
|
|
292
|
+
style,
|
|
293
|
+
hide_on_collision=self.hide_colliding_labels,
|
|
294
|
+
area=constellation.boundary, # TODO : make this intersection with clip path
|
|
295
|
+
auto_adjust_settings=settings,
|
|
296
|
+
gid="constellations-label-name",
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
def _constellation_labels_static(self, style, labels):
|
|
300
|
+
for con in condata.iterator():
|
|
301
|
+
_, ra, dec = condata.get(con)
|
|
302
|
+
text = labels.get(con.lower())
|
|
303
|
+
self.text(
|
|
304
|
+
text,
|
|
305
|
+
ra,
|
|
306
|
+
dec,
|
|
307
|
+
style,
|
|
308
|
+
hide_on_collision=self.hide_colliding_labels,
|
|
309
|
+
gid="constellations-label-name",
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
@profile
|
|
313
|
+
@use_style(LabelStyle, "constellation_labels")
|
|
314
|
+
def constellation_labels(
|
|
315
|
+
self,
|
|
316
|
+
style: LabelStyle = None,
|
|
317
|
+
labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
|
|
318
|
+
auto_adjust: bool = True,
|
|
319
|
+
auto_adjust_settings: dict = DEFAULT_AUTO_ADJUST_SETTINGS,
|
|
320
|
+
):
|
|
321
|
+
"""
|
|
322
|
+
Plots constellation labels.
|
|
323
|
+
|
|
324
|
+
It's good to plot these last because they're area-based labels (vs point-based, like for star names), and area-based labels have more freedom to move around. If you plot area-based labels first, then it would limit the available space for point-based labels.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
style: Styling of the constellation labels. If None, then the plot's style (specified when creating the plot) will be used
|
|
328
|
+
labels: A dictionary where the keys are each constellation's 3-letter IAU abbreviation, and the values are how the constellation will be labeled on the plot.
|
|
329
|
+
auto_adjust: If True (the default), then labels will be automatically adjusted to avoid collisions with other labels and stars **Important: you must plot stars and constellations first for this to work**. This uses a fairly simple method: for each constellation it finds the centroid of all plotted constellation stars with lines and then generates random points in the constellation boundary starting at the centroid and then progressively increasing the distance from the centroid.
|
|
330
|
+
auto_adjust_settings: Optional settings for the auto adjustment algorithm.
|
|
331
|
+
|
|
332
|
+
TODO:
|
|
333
|
+
make this work without plotting constellations first
|
|
334
|
+
|
|
335
|
+
"""
|
|
336
|
+
|
|
337
|
+
if auto_adjust:
|
|
338
|
+
settings = DEFAULT_AUTO_ADJUST_SETTINGS
|
|
339
|
+
settings.update(auto_adjust_settings)
|
|
340
|
+
self._constellation_labels_auto(style, labels, settings=settings)
|
|
341
|
+
else:
|
|
342
|
+
self._constellation_labels_static(style, labels)
|
starplot/plotters/dsos.py
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
from functools import cache
|
|
2
1
|
from typing import Callable, Mapping
|
|
3
2
|
|
|
3
|
+
from ibis import _
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
4
6
|
from starplot.data.dsos import (
|
|
7
|
+
DSO_LABELS_DEFAULT,
|
|
8
|
+
DsoLabelMaker,
|
|
9
|
+
load,
|
|
10
|
+
)
|
|
11
|
+
from starplot.models.dso import (
|
|
12
|
+
DSO,
|
|
5
13
|
DsoType,
|
|
14
|
+
from_tuple,
|
|
6
15
|
ONGC_TYPE_MAP,
|
|
7
16
|
DSO_LEGEND_LABELS,
|
|
8
|
-
DSO_LABELS_DEFAULT,
|
|
9
|
-
DsoLabelMaker,
|
|
10
|
-
load_ongc,
|
|
11
17
|
)
|
|
12
|
-
from starplot.models.dso import DSO, from_tuple
|
|
13
18
|
from starplot.styles import MarkerSymbolEnum
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def _where(*args, **kwargs):
|
|
17
|
-
where = kwargs.pop("where", [])
|
|
18
|
-
|
|
19
|
-
if mag := kwargs.pop("mag", None):
|
|
20
|
-
where.append(DSO.magnitude.is_null() | (DSO.magnitude <= mag))
|
|
21
|
-
return where
|
|
19
|
+
from starplot.profile import profile
|
|
22
20
|
|
|
23
21
|
|
|
24
22
|
class DsoPlotterMixin:
|
|
@@ -29,40 +27,37 @@ class DsoPlotterMixin:
|
|
|
29
27
|
coords.append(coords[0])
|
|
30
28
|
self._polygon(coords, style.marker.to_polygon_style(), closed=False)
|
|
31
29
|
|
|
32
|
-
def messier(self,
|
|
30
|
+
def messier(self, **kwargs):
|
|
33
31
|
"""
|
|
34
32
|
Plots Messier objects
|
|
35
33
|
|
|
36
34
|
This is just a small wrapper around the `dsos()` function, so any `kwargs` will be passed through.
|
|
37
35
|
"""
|
|
38
|
-
where =
|
|
39
|
-
where.append(
|
|
40
|
-
kwargs.pop("where", None)
|
|
36
|
+
where = kwargs.pop("where", [])
|
|
37
|
+
where.append(_.m.notnull())
|
|
41
38
|
self.dsos(where=where, **kwargs)
|
|
42
39
|
|
|
43
|
-
def open_clusters(self,
|
|
40
|
+
def open_clusters(self, **kwargs):
|
|
44
41
|
"""
|
|
45
42
|
Plots open clusters
|
|
46
43
|
|
|
47
44
|
This is just a small wrapper around the `dsos()` function, so any `kwargs` will be passed through.
|
|
48
45
|
"""
|
|
49
|
-
where =
|
|
50
|
-
where.append(
|
|
51
|
-
kwargs.pop("where", None)
|
|
46
|
+
where = kwargs.pop("where", [])
|
|
47
|
+
where.append(_.type == DsoType.OPEN_CLUSTER)
|
|
52
48
|
self.dsos(where=where, **kwargs)
|
|
53
49
|
|
|
54
|
-
def globular_clusters(self,
|
|
50
|
+
def globular_clusters(self, **kwargs):
|
|
55
51
|
"""
|
|
56
52
|
Plots globular clusters
|
|
57
53
|
|
|
58
54
|
This is just a small wrapper around the `dsos()` function, so any `kwargs` will be passed through.
|
|
59
55
|
"""
|
|
60
|
-
where =
|
|
61
|
-
where.append(
|
|
62
|
-
kwargs.pop("where", None)
|
|
56
|
+
where = kwargs.pop("where", [])
|
|
57
|
+
where.append(_.type == DsoType.GLOBULAR_CLUSTER)
|
|
63
58
|
self.dsos(where=where, **kwargs)
|
|
64
59
|
|
|
65
|
-
def galaxies(self,
|
|
60
|
+
def galaxies(self, **kwargs):
|
|
66
61
|
"""
|
|
67
62
|
Plots galaxy DSO types:
|
|
68
63
|
|
|
@@ -77,12 +72,11 @@ class DsoPlotterMixin:
|
|
|
77
72
|
DsoType.GALAXY_PAIR,
|
|
78
73
|
DsoType.GALAXY_TRIPLET,
|
|
79
74
|
]
|
|
80
|
-
where =
|
|
81
|
-
where.append(
|
|
82
|
-
kwargs.pop("where", None)
|
|
75
|
+
where = kwargs.pop("where", [])
|
|
76
|
+
where.append(_.type.isin(galaxy_types))
|
|
83
77
|
self.dsos(where=where, **kwargs)
|
|
84
78
|
|
|
85
|
-
def nebula(self,
|
|
79
|
+
def nebula(self, **kwargs):
|
|
86
80
|
"""
|
|
87
81
|
Plots nebula DSO types:
|
|
88
82
|
|
|
@@ -101,50 +95,39 @@ class DsoPlotterMixin:
|
|
|
101
95
|
DsoType.STAR_CLUSTER_NEBULA,
|
|
102
96
|
DsoType.REFLECTION_NEBULA,
|
|
103
97
|
]
|
|
104
|
-
where =
|
|
105
|
-
where.append(
|
|
106
|
-
kwargs.pop("where", None)
|
|
98
|
+
where = kwargs.pop("where", [])
|
|
99
|
+
where.append(_.type.isin(nebula_types))
|
|
107
100
|
self.dsos(where=where, **kwargs)
|
|
108
101
|
|
|
109
|
-
@
|
|
110
|
-
def _load_dsos(self):
|
|
111
|
-
return load_ongc(bbox=self._extent_mask())
|
|
112
|
-
|
|
102
|
+
@profile
|
|
113
103
|
def dsos(
|
|
114
104
|
self,
|
|
115
|
-
|
|
105
|
+
where: list = None,
|
|
106
|
+
where_labels: list = None,
|
|
116
107
|
true_size: bool = True,
|
|
117
108
|
labels: Mapping[str, str] = DSO_LABELS_DEFAULT,
|
|
118
109
|
legend_labels: Mapping[DsoType, str] = DSO_LEGEND_LABELS,
|
|
119
110
|
alpha_fn: Callable[[DSO], float] = None,
|
|
120
111
|
label_fn: Callable[[DSO], str] = None,
|
|
121
|
-
where: list = None,
|
|
122
|
-
where_labels: list = None,
|
|
123
112
|
):
|
|
124
113
|
"""
|
|
125
114
|
Plots Deep Sky Objects (DSOs), from OpenNGC
|
|
126
115
|
|
|
127
116
|
Args:
|
|
128
|
-
|
|
117
|
+
where: A list of expressions that determine which DSOs to plot. See [Selecting Objects](/reference-selecting-objects/) for details.
|
|
118
|
+
where_labels: A list of expressions that determine which DSOs are labeled on the plot. See [Selecting Objects](/reference-selecting-objects/) for details.
|
|
129
119
|
true_size: If True, then each DSO will be plotted as its true apparent size in the sky (note: this increases plotting time). If False, then the style's marker size will be used. Also, keep in mind not all DSOs have a defined size (according to OpenNGC) -- so these will use the style's marker size.
|
|
130
120
|
labels: A dictionary that maps DSO names (as specified in OpenNGC) to the label that'll be plotted for that object. By default, the DSO's name in OpenNGC will be used as the label. If you want to hide all labels, then set this arg to `None`.
|
|
131
121
|
legend_labels: A dictionary that maps a `DsoType` to the legend label that'll be plotted for that type of DSO. If you want to hide all DSO legend labels, then set this arg to `None`.
|
|
132
122
|
alpha_fn: Callable for calculating the alpha value (aka "opacity") of each DSO. If `None`, then the marker style's alpha will be used.
|
|
133
123
|
label_fn: Callable for determining the label of each DSO. If `None`, then the names in the `labels` kwarg will be used.
|
|
134
|
-
where: A list of expressions that determine which DSOs to plot. See [Selecting Objects](/reference-selecting-objects/) for details.
|
|
135
|
-
where_labels: A list of expressions that determine which DSOs are labeled on the plot. See [Selecting Objects](/reference-selecting-objects/) for details.
|
|
136
124
|
"""
|
|
137
125
|
|
|
138
126
|
# TODO: add kwarg styles
|
|
139
127
|
|
|
140
|
-
self.logger.debug("Plotting DSOs...")
|
|
141
|
-
|
|
142
128
|
where = where or []
|
|
143
129
|
where_labels = where_labels or []
|
|
144
130
|
|
|
145
|
-
if not where:
|
|
146
|
-
where = [DSO.magnitude.is_null() | (DSO.magnitude <= mag)]
|
|
147
|
-
|
|
148
131
|
if labels is None:
|
|
149
132
|
labels = {}
|
|
150
133
|
elif type(labels) != DsoLabelMaker:
|
|
@@ -155,35 +138,35 @@ class DsoPlotterMixin:
|
|
|
155
138
|
else:
|
|
156
139
|
legend_labels = {**DSO_LEGEND_LABELS, **legend_labels}
|
|
157
140
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
141
|
+
extent = self._extent_mask()
|
|
142
|
+
dso_results = load(extent=extent, filters=where)
|
|
143
|
+
|
|
144
|
+
dso_results_labeled = dso_results
|
|
145
|
+
for f in where_labels:
|
|
146
|
+
dso_results_labeled = dso_results_labeled.filter(f)
|
|
147
|
+
|
|
148
|
+
label_row_ids = dso_results_labeled.to_pandas()["rowid"].tolist()
|
|
149
|
+
|
|
150
|
+
results_df = dso_results.to_pandas()
|
|
151
|
+
results_df = results_df.replace({np.nan: None})
|
|
161
152
|
|
|
162
|
-
for d in
|
|
153
|
+
for d in results_df.itertuples():
|
|
163
154
|
ra = d.ra_degrees
|
|
164
155
|
dec = d.dec_degrees
|
|
165
156
|
dso_type = ONGC_TYPE_MAP[d.type]
|
|
166
157
|
style = self.style.get_dso_style(dso_type)
|
|
167
158
|
maj_ax, min_ax, angle = d.maj_ax, d.min_ax, d.angle
|
|
168
159
|
legend_label = legend_labels.get(dso_type)
|
|
169
|
-
magnitude = d.mag_v or d.mag_b or None
|
|
170
|
-
magnitude = float(magnitude) if magnitude else None
|
|
171
160
|
_dso = from_tuple(d)
|
|
172
161
|
label = labels.get(d.name) if label_fn is None else label_fn(_dso)
|
|
173
162
|
|
|
174
|
-
if
|
|
175
|
-
[
|
|
176
|
-
style is None,
|
|
177
|
-
not all([e.evaluate(_dso) for e in where]),
|
|
178
|
-
# not self.in_bounds(ra / 15, dec),
|
|
179
|
-
]
|
|
180
|
-
):
|
|
163
|
+
if style is None:
|
|
181
164
|
continue
|
|
182
165
|
|
|
183
166
|
_alpha_fn = alpha_fn or (lambda d: style.marker.alpha)
|
|
184
167
|
style.marker.alpha = _alpha_fn(_dso)
|
|
185
168
|
|
|
186
|
-
if
|
|
169
|
+
if _dso._row_id not in label_row_ids:
|
|
187
170
|
label = None
|
|
188
171
|
|
|
189
172
|
if true_size and d.size_deg2 is not None:
|
|
@@ -206,7 +189,7 @@ class DsoPlotterMixin:
|
|
|
206
189
|
|
|
207
190
|
if style.marker.symbol == MarkerSymbolEnum.SQUARE:
|
|
208
191
|
self.rectangle(
|
|
209
|
-
(ra
|
|
192
|
+
(ra, dec),
|
|
210
193
|
min_ax_degrees * 2,
|
|
211
194
|
maj_ax_degrees * 2,
|
|
212
195
|
style=poly_style,
|
|
@@ -214,7 +197,7 @@ class DsoPlotterMixin:
|
|
|
214
197
|
)
|
|
215
198
|
else:
|
|
216
199
|
self.ellipse(
|
|
217
|
-
(ra
|
|
200
|
+
(ra, dec),
|
|
218
201
|
min_ax_degrees * 2,
|
|
219
202
|
maj_ax_degrees * 2,
|
|
220
203
|
style=poly_style,
|
|
@@ -222,14 +205,12 @@ class DsoPlotterMixin:
|
|
|
222
205
|
)
|
|
223
206
|
|
|
224
207
|
if label:
|
|
225
|
-
self.text(
|
|
226
|
-
label, ra / 15, dec, style.label, gid=f"dso-{d.type}-label"
|
|
227
|
-
)
|
|
208
|
+
self.text(label, ra, dec, style.label, gid=f"dso-{d.type}-label")
|
|
228
209
|
|
|
229
210
|
else:
|
|
230
211
|
# if no major axis, then just plot as a marker
|
|
231
212
|
self.marker(
|
|
232
|
-
ra=ra
|
|
213
|
+
ra=ra,
|
|
233
214
|
dec=dec,
|
|
234
215
|
style=style,
|
|
235
216
|
label=label,
|