starplot 0.14.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 +3 -1
- starplot/base.py +149 -37
- starplot/cli.py +33 -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 +43 -32
- 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 +105 -6
- starplot/horizon.py +118 -107
- starplot/map.py +45 -96
- starplot/mixins.py +75 -14
- starplot/models/__init__.py +1 -1
- starplot/models/base.py +10 -128
- 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 +14 -17
- starplot/plotters/constellations.py +81 -78
- starplot/plotters/dsos.py +49 -68
- starplot/plotters/experimental.py +1 -1
- starplot/plotters/milkyway.py +18 -20
- starplot/plotters/stars.py +91 -116
- starplot/profile.py +16 -0
- starplot/settings.py +26 -0
- starplot/styles/__init__.py +2 -0
- starplot/styles/base.py +7 -17
- starplot/styles/ext/blue_gold.yml +135 -0
- starplot/styles/ext/blue_light.yml +5 -4
- starplot/styles/ext/blue_medium.yml +11 -7
- starplot/styles/extensions.py +1 -0
- starplot/warnings.py +5 -0
- {starplot-0.14.0.dist-info → starplot-0.15.0.dist-info}/METADATA +11 -11
- {starplot-0.14.0.dist-info → starplot-0.15.0.dist-info}/RECORD +44 -54
- 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.14.0.dist-info → starplot-0.15.0.dist-info}/LICENSE +0 -0
- {starplot-0.14.0.dist-info → starplot-0.15.0.dist-info}/WHEEL +0 -0
starplot/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Star charts and maps of the sky"""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.
|
|
3
|
+
__version__ = "0.15.0"
|
|
4
4
|
|
|
5
5
|
from .base import BasePlot # noqa: F401
|
|
6
6
|
from .map import MapPlot, Projection # noqa: F401
|
|
@@ -8,6 +8,7 @@ from .horizon import HorizonPlot # noqa: F401
|
|
|
8
8
|
from .optic import OpticPlot # noqa: F401
|
|
9
9
|
from .models import (
|
|
10
10
|
DSO, # noqa: F401
|
|
11
|
+
DsoType, # noqa: F401
|
|
11
12
|
Star, # noqa: F401
|
|
12
13
|
Constellation, # noqa: F401
|
|
13
14
|
Planet, # noqa: F401
|
|
@@ -16,3 +17,4 @@ from .models import (
|
|
|
16
17
|
ObjectList, # noqa: F401
|
|
17
18
|
)
|
|
18
19
|
from .styles import * # noqa: F401 F403
|
|
20
|
+
from ibis import _ # noqa: F401 F403
|
starplot/base.py
CHANGED
|
@@ -33,10 +33,10 @@ from starplot.styles import (
|
|
|
33
33
|
)
|
|
34
34
|
from starplot.styles.helpers import use_style
|
|
35
35
|
from starplot.geometry import (
|
|
36
|
-
|
|
36
|
+
unwrap_polygon_360,
|
|
37
37
|
random_point_in_polygon_at_distance,
|
|
38
38
|
)
|
|
39
|
-
|
|
39
|
+
from starplot.profile import profile
|
|
40
40
|
|
|
41
41
|
LOGGER = logging.getLogger("starplot")
|
|
42
42
|
LOG_HANDLER = logging.StreamHandler()
|
|
@@ -48,7 +48,11 @@ LOGGER.addHandler(LOG_HANDLER)
|
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
DEFAULT_FOV_STYLE = PolygonStyle(
|
|
51
|
-
fill_color=None,
|
|
51
|
+
fill_color=None,
|
|
52
|
+
edge_color="red",
|
|
53
|
+
line_style=[1, [2, 3]],
|
|
54
|
+
edge_width=3,
|
|
55
|
+
zorder=-1000,
|
|
52
56
|
)
|
|
53
57
|
"""Default style for plotting scope and bino field of view circles"""
|
|
54
58
|
|
|
@@ -61,6 +65,7 @@ DPI = 100
|
|
|
61
65
|
|
|
62
66
|
class BasePlot(ABC):
|
|
63
67
|
_background_clip_path = None
|
|
68
|
+
_clip_path_polygon: Polygon = None # clip path in display coordinates
|
|
64
69
|
_coordinate_system = CoordinateSystem.RA_DEC
|
|
65
70
|
|
|
66
71
|
def __init__(
|
|
@@ -95,11 +100,13 @@ class BasePlot(ABC):
|
|
|
95
100
|
self.dt = dt or timezone("UTC").localize(datetime.now())
|
|
96
101
|
self._ephemeris_name = ephemeris
|
|
97
102
|
self.ephemeris = load(ephemeris)
|
|
103
|
+
self.earth = self.ephemeris["earth"]
|
|
98
104
|
|
|
99
105
|
self.labels = []
|
|
100
106
|
self._labels_rtree = rtree.index.Index()
|
|
101
107
|
self._constellations_rtree = rtree.index.Index()
|
|
102
108
|
self._stars_rtree = rtree.index.Index()
|
|
109
|
+
self._markers_rtree = rtree.index.Index()
|
|
103
110
|
|
|
104
111
|
self._background_clip_path = None
|
|
105
112
|
|
|
@@ -126,6 +133,10 @@ class BasePlot(ABC):
|
|
|
126
133
|
def _prepare_coords(self, ra, dec) -> tuple[float, float]:
|
|
127
134
|
return ra, dec
|
|
128
135
|
|
|
136
|
+
def _update_clip_path_polygon(self, buffer=8):
|
|
137
|
+
coords = self._background_clip_path.get_verts()
|
|
138
|
+
self._clip_path_polygon = Polygon(coords).buffer(-1 * buffer)
|
|
139
|
+
|
|
129
140
|
def _is_label_collision(self, bbox) -> bool:
|
|
130
141
|
ix = list(self._labels_rtree.intersection(bbox))
|
|
131
142
|
return len(ix) > 0
|
|
@@ -138,11 +149,18 @@ class BasePlot(ABC):
|
|
|
138
149
|
ix = list(self._stars_rtree.intersection(bbox))
|
|
139
150
|
return len(ix) > 0
|
|
140
151
|
|
|
152
|
+
def _is_marker_collision(self, bbox) -> bool:
|
|
153
|
+
ix = list(self._markers_rtree.intersection(bbox))
|
|
154
|
+
return len(ix) > 0
|
|
155
|
+
|
|
141
156
|
def _is_clipped(self, points) -> bool:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
157
|
+
p = self._clip_path_polygon
|
|
158
|
+
|
|
159
|
+
for x, y in points:
|
|
160
|
+
if not p.contains(Point(x, y)):
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
return False
|
|
146
164
|
|
|
147
165
|
def _add_label_to_rtree(self, label, extent=None):
|
|
148
166
|
extent = extent or label.get_window_extent(
|
|
@@ -153,6 +171,53 @@ class BasePlot(ABC):
|
|
|
153
171
|
0, np.array((extent.x0 - 1, extent.y0 - 1, extent.x1 + 1, extent.y1 + 1))
|
|
154
172
|
)
|
|
155
173
|
|
|
174
|
+
def _is_open_space(
|
|
175
|
+
self,
|
|
176
|
+
bbox: tuple[float, float, float, float],
|
|
177
|
+
padding=0,
|
|
178
|
+
avoid_clipped=True,
|
|
179
|
+
avoid_label_collisions=True,
|
|
180
|
+
avoid_marker_collisions=True,
|
|
181
|
+
avoid_constellation_collision=True,
|
|
182
|
+
) -> bool:
|
|
183
|
+
"""
|
|
184
|
+
Returns true if the boox covers an open space (i.e. no collisions)
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
bbox: 4-element tuple of lower left and upper right coordinates
|
|
188
|
+
"""
|
|
189
|
+
x0, y0, x1, y1 = bbox
|
|
190
|
+
points = [(x0, y0), (x1, y1)]
|
|
191
|
+
bbox = (
|
|
192
|
+
x0 - padding,
|
|
193
|
+
y0 - padding,
|
|
194
|
+
x1 + padding,
|
|
195
|
+
y1 + padding,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if any([np.isnan(c) for c in (x0, y0, x1, y1)]):
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
if avoid_clipped and self._is_clipped(points):
|
|
202
|
+
return False
|
|
203
|
+
|
|
204
|
+
if avoid_label_collisions and self._is_label_collision(bbox):
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
if avoid_marker_collisions and (
|
|
208
|
+
self._is_star_collision(bbox) or self._is_marker_collision(bbox)
|
|
209
|
+
):
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
if avoid_constellation_collision and self._is_constellation_collision(bbox):
|
|
213
|
+
return False
|
|
214
|
+
|
|
215
|
+
return True
|
|
216
|
+
|
|
217
|
+
def _get_label_bbox(self, label):
|
|
218
|
+
extent = label.get_window_extent(renderer=self.fig.canvas.get_renderer())
|
|
219
|
+
return (extent.x0, extent.y0, extent.x1, extent.y1)
|
|
220
|
+
|
|
156
221
|
def _maybe_remove_label(
|
|
157
222
|
self,
|
|
158
223
|
label,
|
|
@@ -171,11 +236,6 @@ class BasePlot(ABC):
|
|
|
171
236
|
)
|
|
172
237
|
points = [(extent.x0, extent.y0), (extent.x1, extent.y1)]
|
|
173
238
|
|
|
174
|
-
# if label.get_text() == "CANIS MAJOR":
|
|
175
|
-
# print(bbox)
|
|
176
|
-
# if label.get_text() == "Electra":
|
|
177
|
-
# print(bbox)
|
|
178
|
-
|
|
179
239
|
if any([np.isnan(c) for c in (extent.x0, extent.y0, extent.x1, extent.y1)]):
|
|
180
240
|
label.remove()
|
|
181
241
|
return True
|
|
@@ -185,7 +245,9 @@ class BasePlot(ABC):
|
|
|
185
245
|
return True
|
|
186
246
|
|
|
187
247
|
if remove_on_collision and (
|
|
188
|
-
self._is_label_collision(bbox)
|
|
248
|
+
self._is_label_collision(bbox)
|
|
249
|
+
or self._is_star_collision(bbox)
|
|
250
|
+
or self._is_marker_collision(bbox)
|
|
189
251
|
):
|
|
190
252
|
label.remove()
|
|
191
253
|
return True
|
|
@@ -344,32 +406,41 @@ class BasePlot(ABC):
|
|
|
344
406
|
**kwargs,
|
|
345
407
|
) -> None:
|
|
346
408
|
kwargs["path_effects"] = kwargs.get("path_effects", [self.text_border])
|
|
409
|
+
kwargs["va"] = "center"
|
|
410
|
+
kwargs["ha"] = "center"
|
|
347
411
|
|
|
348
412
|
avoid_constellation_lines = settings.get("avoid_constellation_lines", False)
|
|
349
413
|
padding = settings.get("label_padding", 3)
|
|
350
|
-
|
|
414
|
+
settings.get("buffer", 0.1)
|
|
351
415
|
max_distance = settings.get("max_distance", 300)
|
|
352
416
|
distance_step_size = settings.get("distance_step_size", 1)
|
|
353
417
|
point_iterations = settings.get("point_generation_max_iterations", 500)
|
|
354
418
|
random_seed = settings.get("seed")
|
|
355
419
|
|
|
420
|
+
attempts = 0
|
|
421
|
+
height = None
|
|
422
|
+
width = None
|
|
423
|
+
bbox = None
|
|
356
424
|
areas = (
|
|
357
425
|
[p for p in area.geoms] if "MultiPolygon" == str(area.geom_type) else [area]
|
|
358
426
|
)
|
|
359
427
|
new_areas = []
|
|
428
|
+
origin = Point(ra, dec)
|
|
360
429
|
|
|
361
430
|
for a in areas:
|
|
362
|
-
unwrapped =
|
|
363
|
-
|
|
364
|
-
|
|
431
|
+
unwrapped = unwrap_polygon_360(a)
|
|
432
|
+
# new_buffer = unwrapped.area / 10 * -1 * buffer * self.scale
|
|
433
|
+
# new_buffer = -1 * buffer * self.scale
|
|
434
|
+
# new_poly = unwrapped.buffer(new_buffer)
|
|
435
|
+
new_areas.append(unwrapped)
|
|
365
436
|
|
|
366
437
|
for d in range(0, max_distance, distance_step_size):
|
|
367
|
-
distance = d /
|
|
438
|
+
distance = d / 20
|
|
368
439
|
poly = randrange(len(new_areas))
|
|
369
440
|
point = random_point_in_polygon_at_distance(
|
|
370
441
|
new_areas[poly],
|
|
371
|
-
|
|
372
|
-
distance,
|
|
442
|
+
origin_point=origin,
|
|
443
|
+
distance=distance,
|
|
373
444
|
max_iterations=point_iterations,
|
|
374
445
|
seed=random_seed,
|
|
375
446
|
)
|
|
@@ -378,20 +449,45 @@ class BasePlot(ABC):
|
|
|
378
449
|
continue
|
|
379
450
|
|
|
380
451
|
x, y = self._prepare_coords(point.x, point.y)
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
452
|
+
|
|
453
|
+
if height and width:
|
|
454
|
+
data_xy = self._proj.transform_point(x, y, self._crs)
|
|
455
|
+
display_x, display_y = self.ax.transData.transform(data_xy)
|
|
456
|
+
bbox = (
|
|
457
|
+
display_x - width / 2,
|
|
458
|
+
display_y - height / 2,
|
|
459
|
+
display_x + width / 2,
|
|
460
|
+
display_y + height / 2,
|
|
461
|
+
)
|
|
462
|
+
label = None
|
|
463
|
+
|
|
464
|
+
else:
|
|
465
|
+
label = self._text(x, y, text, **kwargs)
|
|
466
|
+
bbox = self._get_label_bbox(label)
|
|
467
|
+
height = bbox[3] - bbox[1]
|
|
468
|
+
width = bbox[2] - bbox[0]
|
|
469
|
+
|
|
470
|
+
is_open = self._is_open_space(
|
|
471
|
+
bbox,
|
|
387
472
|
padding=padding,
|
|
473
|
+
avoid_clipped=clip_on,
|
|
474
|
+
avoid_constellation_collision=avoid_constellation_lines,
|
|
475
|
+
avoid_marker_collisions=hide_on_collision,
|
|
476
|
+
avoid_label_collisions=hide_on_collision,
|
|
388
477
|
)
|
|
389
478
|
|
|
390
|
-
# TODO : remove label if not fully inside area?
|
|
479
|
+
# # TODO : remove label if not fully inside area?
|
|
480
|
+
|
|
481
|
+
attempts += 1
|
|
482
|
+
|
|
483
|
+
if is_open and label is None:
|
|
484
|
+
label = self._text(x, y, text, **kwargs)
|
|
391
485
|
|
|
392
|
-
if
|
|
486
|
+
if is_open:
|
|
393
487
|
self._add_label_to_rtree(label)
|
|
394
488
|
return label
|
|
489
|
+
elif label is not None:
|
|
490
|
+
label.remove()
|
|
395
491
|
|
|
396
492
|
@use_style(LabelStyle)
|
|
397
493
|
def text(
|
|
@@ -522,6 +618,7 @@ class BasePlot(ABC):
|
|
|
522
618
|
if self.fig:
|
|
523
619
|
plt.close(self.fig)
|
|
524
620
|
|
|
621
|
+
@profile
|
|
525
622
|
def export(self, filename: str, format: str = "png", padding: float = 0, **kwargs):
|
|
526
623
|
"""Exports the plot to an image file.
|
|
527
624
|
|
|
@@ -568,18 +665,35 @@ class BasePlot(ABC):
|
|
|
568
665
|
if not skip_bounds_check and not self.in_bounds(ra, dec):
|
|
569
666
|
return
|
|
570
667
|
|
|
668
|
+
# Plot marker
|
|
571
669
|
x, y = self._prepare_coords(ra, dec)
|
|
572
|
-
|
|
670
|
+
style_kwargs = style.marker.matplot_scatter_kwargs(self.scale)
|
|
573
671
|
self.ax.scatter(
|
|
574
672
|
x,
|
|
575
673
|
y,
|
|
576
|
-
**
|
|
674
|
+
**style_kwargs,
|
|
577
675
|
**self._plot_kwargs(),
|
|
578
676
|
clip_on=True,
|
|
579
677
|
clip_path=self._background_clip_path,
|
|
580
678
|
gid=kwargs.get("gid_marker") or "marker",
|
|
581
679
|
)
|
|
582
680
|
|
|
681
|
+
# Add to spatial index
|
|
682
|
+
data_xy = self._proj.transform_point(x, y, self._crs)
|
|
683
|
+
display_x, display_y = self.ax.transData.transform(data_xy)
|
|
684
|
+
if display_x > 0 and display_y > 0:
|
|
685
|
+
radius = style_kwargs.get("s", 1) ** 0.5 / 5
|
|
686
|
+
bbox = np.array(
|
|
687
|
+
(
|
|
688
|
+
display_x - radius,
|
|
689
|
+
display_y - radius,
|
|
690
|
+
display_x + radius,
|
|
691
|
+
display_y + radius,
|
|
692
|
+
)
|
|
693
|
+
)
|
|
694
|
+
self._markers_rtree.insert(0, bbox, None)
|
|
695
|
+
|
|
696
|
+
# Plot label
|
|
583
697
|
if label:
|
|
584
698
|
label_style = style.label
|
|
585
699
|
if label_style.offset_x == "auto" or label_style.offset_y == "auto":
|
|
@@ -748,7 +862,6 @@ class BasePlot(ABC):
|
|
|
748
862
|
raise NotImplementedError
|
|
749
863
|
|
|
750
864
|
def _polygon(self, points: list, style: PolygonStyle, **kwargs):
|
|
751
|
-
points = [geod.to_radec(p) for p in points]
|
|
752
865
|
points = [self._prepare_coords(*p) for p in points]
|
|
753
866
|
patch = patches.Polygon(
|
|
754
867
|
points,
|
|
@@ -787,8 +900,7 @@ class BasePlot(ABC):
|
|
|
787
900
|
if geometry is not None:
|
|
788
901
|
points = list(zip(*geometry.exterior.coords.xy))
|
|
789
902
|
|
|
790
|
-
|
|
791
|
-
self._polygon(_points, style, gid=kwargs.get("gid") or "polygon")
|
|
903
|
+
self._polygon(points, style, gid=kwargs.get("gid") or "polygon")
|
|
792
904
|
|
|
793
905
|
if legend_label is not None:
|
|
794
906
|
self._add_legend_handle_marker(
|
|
@@ -1156,11 +1268,11 @@ class BasePlot(ABC):
|
|
|
1156
1268
|
inbounds = []
|
|
1157
1269
|
|
|
1158
1270
|
for ra, dec in ecliptic.RA_DECS:
|
|
1159
|
-
x0, y0 = self._prepare_coords(ra, dec)
|
|
1271
|
+
x0, y0 = self._prepare_coords(ra * 15, dec)
|
|
1160
1272
|
x.append(x0)
|
|
1161
1273
|
y.append(y0)
|
|
1162
|
-
if self.in_bounds(ra, dec):
|
|
1163
|
-
inbounds.append((ra, dec))
|
|
1274
|
+
if self.in_bounds(ra * 15, dec):
|
|
1275
|
+
inbounds.append((ra * 15, dec))
|
|
1164
1276
|
|
|
1165
1277
|
self.ax.plot(
|
|
1166
1278
|
x,
|
|
@@ -1195,7 +1307,7 @@ class BasePlot(ABC):
|
|
|
1195
1307
|
# TODO : handle wrapping
|
|
1196
1308
|
|
|
1197
1309
|
for ra in range(25):
|
|
1198
|
-
x0, y0 = self._prepare_coords(ra, 0)
|
|
1310
|
+
x0, y0 = self._prepare_coords(ra * 15, 0)
|
|
1199
1311
|
x.append(x0)
|
|
1200
1312
|
y.append(y0)
|
|
1201
1313
|
|
starplot/cli.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from starplot.styles import fonts
|
|
4
|
+
|
|
5
|
+
COMMANDS = ["setup"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def setup(options):
|
|
9
|
+
from starplot import settings
|
|
10
|
+
from starplot.data import db, bigsky
|
|
11
|
+
|
|
12
|
+
print("Installing DuckDB spatial extension...")
|
|
13
|
+
|
|
14
|
+
con = db.connect()
|
|
15
|
+
con.load_extension("spatial")
|
|
16
|
+
|
|
17
|
+
fonts.load()
|
|
18
|
+
|
|
19
|
+
print(f"Installed to: {settings.DUCKDB_EXTENSION_PATH}")
|
|
20
|
+
|
|
21
|
+
if "--install-big-sky" in options:
|
|
22
|
+
bigsky.download_if_not_exists()
|
|
23
|
+
print(f"Big Sky Catalog downloaded and installed to: {settings.DOWNLOAD_PATH}")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def main():
|
|
27
|
+
command = sys.argv[1].lower()
|
|
28
|
+
|
|
29
|
+
if command not in COMMANDS:
|
|
30
|
+
print(f"Unrecognized command: {command}")
|
|
31
|
+
|
|
32
|
+
if command == "setup":
|
|
33
|
+
setup(sys.argv[2:])
|
starplot/data/__init__.py
CHANGED
|
@@ -1,31 +1,13 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from enum import Enum
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
1
|
from skyfield.api import Loader
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
DATA_PATH = HERE / "library"
|
|
10
|
-
|
|
11
|
-
load = Loader(DATA_PATH)
|
|
3
|
+
from starplot import settings
|
|
12
4
|
|
|
5
|
+
load = Loader(settings.DATA_PATH)
|
|
13
6
|
|
|
14
|
-
def env(name, default):
|
|
15
|
-
return os.environ.get(name) or default
|
|
16
7
|
|
|
8
|
+
class DataFiles:
|
|
9
|
+
BIG_SKY = settings.DOWNLOAD_PATH / "bigsky.0.4.0.stars.parquet"
|
|
17
10
|
|
|
18
|
-
|
|
19
|
-
# Built-In Files
|
|
20
|
-
CONSTELLATION_LINES = DATA_PATH / "constellation_lines_inv.gpkg"
|
|
21
|
-
CONSTELLATION_LINES_HIP = DATA_PATH / "constellation_lines_hips.json"
|
|
22
|
-
CONSTELLATION_BORDERS = DATA_PATH / "constellation_borders_inv.gpkg"
|
|
23
|
-
MILKY_WAY = DATA_PATH / "milkyway.gpkg"
|
|
24
|
-
HIPPARCOS = DATA_PATH / "stars.hipparcos.parquet"
|
|
25
|
-
BIG_SKY_MAG11 = DATA_PATH / "stars.bigsky.mag11.parquet"
|
|
26
|
-
ONGC = DATA_PATH / "ongc.gpkg.zip"
|
|
27
|
-
CONSTELLATIONS = DATA_PATH / "constellations.gpkg"
|
|
11
|
+
BIG_SKY_MAG11 = settings.DATA_PATH / "bigsky.0.4.0.stars.mag11.parquet"
|
|
28
12
|
|
|
29
|
-
|
|
30
|
-
_DOWNLOAD_PATH = Path(env("STARPLOT_DOWNLOAD_PATH", str(DATA_PATH)))
|
|
31
|
-
BIG_SKY = _DOWNLOAD_PATH / "stars.bigsky.parquet"
|
|
13
|
+
DATABASE = settings.DATA_PATH / "sky.db"
|
starplot/data/bigsky.py
CHANGED
|
@@ -1,56 +1,58 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
import pandas as pd
|
|
4
5
|
|
|
5
|
-
from starplot
|
|
6
|
+
from starplot import settings
|
|
7
|
+
from starplot.data import DataFiles, utils
|
|
6
8
|
|
|
7
9
|
|
|
8
|
-
BIG_SKY_VERSION = "0.
|
|
10
|
+
BIG_SKY_VERSION = "0.4.0"
|
|
11
|
+
BIG_SKY_FILENAME = f"bigsky.{BIG_SKY_VERSION}.stars.csv.gz"
|
|
12
|
+
BIG_SKY_PQ_FILENAME = f"bigsky.{BIG_SKY_VERSION}.stars.parquet"
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
BIG_SKY_MAG11_FILENAME = f"bigsky.{BIG_SKY_VERSION}.stars.mag11.csv.gz"
|
|
15
|
+
BIG_SKY_MAG11_PQ_FILENAME = f"bigsky.{BIG_SKY_VERSION}.stars.mag11.parquet"
|
|
11
16
|
|
|
12
|
-
BIG_SKY_URL = f"https://github.com/steveberardi/bigsky/releases/download/v{BIG_SKY_VERSION}/{BIG_SKY_FILENAME}"
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
DIGITS = 4
|
|
17
|
-
|
|
18
|
-
BIG_SKY_ASSETS = {
|
|
19
|
-
DataFiles.BIG_SKY: "bigsky.stars.csv.gz",
|
|
20
|
-
DataFiles.BIG_SKY_MAG11: "bigsky.stars.mag11.csv.gz",
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def url(filename: str, version: str):
|
|
18
|
+
def get_url(version: str = BIG_SKY_VERSION, filename: str = BIG_SKY_FILENAME):
|
|
25
19
|
return f"https://github.com/steveberardi/bigsky/releases/download/v{version}/{filename}"
|
|
26
20
|
|
|
27
21
|
|
|
28
22
|
def download(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
url: str = None,
|
|
24
|
+
download_path: str = settings.DOWNLOAD_PATH,
|
|
25
|
+
download_filename: str = BIG_SKY_FILENAME,
|
|
26
|
+
build_file: str = DataFiles.BIG_SKY,
|
|
33
27
|
):
|
|
34
|
-
|
|
28
|
+
url = url or get_url()
|
|
29
|
+
download_path = Path(download_path)
|
|
30
|
+
|
|
31
|
+
if not os.path.exists(download_path):
|
|
32
|
+
os.makedirs(download_path)
|
|
33
|
+
|
|
34
|
+
full_download_path = download_path / download_filename
|
|
35
35
|
utils.download(
|
|
36
|
-
url
|
|
37
|
-
|
|
36
|
+
url,
|
|
37
|
+
full_download_path,
|
|
38
38
|
"Big Sky Star Catalog",
|
|
39
39
|
)
|
|
40
40
|
to_parquet(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
digits,
|
|
41
|
+
full_download_path,
|
|
42
|
+
build_file,
|
|
44
43
|
)
|
|
45
44
|
|
|
46
45
|
|
|
47
|
-
def to_parquet(source_path: str, destination_path: str
|
|
46
|
+
def to_parquet(source_path: str, destination_path: str):
|
|
47
|
+
import pyarrow as pa
|
|
48
|
+
import pyarrow.parquet as pq
|
|
49
|
+
|
|
48
50
|
print("Preparing Big Sky Catalog for Starplot...")
|
|
49
51
|
|
|
50
52
|
df = pd.read_csv(
|
|
51
53
|
source_path,
|
|
52
54
|
header=0,
|
|
53
|
-
|
|
55
|
+
usecols=[
|
|
54
56
|
"tyc_id",
|
|
55
57
|
"hip_id",
|
|
56
58
|
"ccdm",
|
|
@@ -61,14 +63,11 @@ def to_parquet(source_path: str, destination_path: str, digits: int = DIGITS):
|
|
|
61
63
|
"ra_mas_per_year",
|
|
62
64
|
"dec_mas_per_year",
|
|
63
65
|
"parallax_mas",
|
|
66
|
+
"constellation",
|
|
64
67
|
],
|
|
65
68
|
compression="gzip",
|
|
66
69
|
)
|
|
67
70
|
|
|
68
|
-
df["ra_hours"] = df.apply(
|
|
69
|
-
lambda row: round(row.ra_degrees_j2000 / 15, digits), axis=1
|
|
70
|
-
)
|
|
71
|
-
|
|
72
71
|
df = df.assign(epoch_year=2000)
|
|
73
72
|
|
|
74
73
|
df = df.rename(
|
|
@@ -79,19 +78,38 @@ def to_parquet(source_path: str, destination_path: str, digits: int = DIGITS):
|
|
|
79
78
|
}
|
|
80
79
|
)
|
|
81
80
|
|
|
82
|
-
df.
|
|
83
|
-
|
|
84
|
-
print(f"Done! {destination_path.value}")
|
|
81
|
+
df = df.sort_values(["magnitude"])
|
|
85
82
|
|
|
83
|
+
table = pa.Table.from_pandas(df)
|
|
84
|
+
table = table.drop_columns("__index_level_0__")
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
pq.write_table(
|
|
87
|
+
table,
|
|
88
|
+
destination_path,
|
|
89
|
+
compression="none",
|
|
90
|
+
sorting_columns=[
|
|
91
|
+
pq.SortingColumn(df.columns.get_loc("magnitude")),
|
|
92
|
+
],
|
|
93
|
+
)
|
|
92
94
|
|
|
93
|
-
|
|
95
|
+
print(f"Done! {destination_path}")
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
def exists(path) -> bool:
|
|
97
99
|
return os.path.isfile(path)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def download_if_not_exists(
|
|
103
|
+
filename: str = DataFiles.BIG_SKY,
|
|
104
|
+
url: str = None,
|
|
105
|
+
download_path: str = settings.DOWNLOAD_PATH,
|
|
106
|
+
download_filename: str = BIG_SKY_FILENAME,
|
|
107
|
+
build_file: str = DataFiles.BIG_SKY,
|
|
108
|
+
):
|
|
109
|
+
if not exists(filename):
|
|
110
|
+
download(
|
|
111
|
+
url=url,
|
|
112
|
+
download_path=download_path,
|
|
113
|
+
download_filename=download_filename,
|
|
114
|
+
build_file=build_file,
|
|
115
|
+
)
|