starplot 0.12.5__py2.py3-none-any.whl → 0.14.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.
Potentially problematic release.
This version of starplot might be problematic. Click here for more details.
- starplot/__init__.py +3 -2
- starplot/base.py +408 -95
- starplot/callables.py +61 -7
- starplot/coordinates.py +6 -0
- starplot/data/bayer.py +1532 -3
- starplot/data/constellations.py +564 -2
- starplot/data/flamsteed.py +2682 -0
- starplot/data/library/constellation_borders_inv.gpkg +0 -0
- starplot/data/library/constellation_lines_hips.json +3 -1
- starplot/data/stars.py +408 -87
- starplot/geometry.py +82 -0
- starplot/horizon.py +458 -0
- starplot/map.py +97 -284
- starplot/models/base.py +9 -2
- starplot/models/constellation.py +1 -1
- starplot/optic.py +32 -14
- starplot/plotters/__init__.py +2 -0
- starplot/plotters/constellations.py +339 -0
- starplot/plotters/dsos.py +5 -1
- starplot/plotters/experimental.py +171 -0
- starplot/plotters/milkyway.py +41 -0
- starplot/plotters/stars.py +143 -13
- starplot/styles/base.py +308 -169
- starplot/styles/ext/antique.yml +54 -46
- starplot/styles/ext/blue_dark.yml +39 -45
- starplot/styles/ext/blue_light.yml +49 -30
- starplot/styles/ext/blue_medium.yml +53 -50
- starplot/styles/ext/cb_wong.yml +16 -7
- starplot/styles/ext/grayscale.yml +17 -10
- starplot/styles/ext/grayscale_dark.yml +18 -8
- starplot/styles/ext/map.yml +10 -7
- starplot/styles/ext/nord.yml +38 -38
- starplot/styles/ext/optic.yml +7 -5
- starplot/styles/fonts-library/gfs-didot/DESCRIPTION.en_us.html +9 -0
- starplot/styles/fonts-library/gfs-didot/GFSDidot-Regular.ttf +0 -0
- starplot/styles/fonts-library/gfs-didot/METADATA.pb +16 -0
- starplot/styles/fonts-library/gfs-didot/OFL.txt +94 -0
- starplot/styles/fonts-library/hind/DESCRIPTION.en_us.html +28 -0
- starplot/styles/fonts-library/hind/Hind-Bold.ttf +0 -0
- starplot/styles/fonts-library/hind/Hind-Light.ttf +0 -0
- starplot/styles/fonts-library/hind/Hind-Medium.ttf +0 -0
- starplot/styles/fonts-library/hind/Hind-Regular.ttf +0 -0
- starplot/styles/fonts-library/hind/Hind-SemiBold.ttf +0 -0
- starplot/styles/fonts-library/hind/METADATA.pb +58 -0
- starplot/styles/fonts-library/hind/OFL.txt +93 -0
- starplot/styles/fonts-library/inter/Inter-Black.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-BlackItalic.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-Bold.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-BoldItalic.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-ExtraBold.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-ExtraBoldItalic.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-ExtraLight.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-ExtraLightItalic.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-Italic.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-Light.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-LightItalic.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-Medium.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-MediumItalic.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-Regular.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-SemiBold.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-SemiBoldItalic.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-Thin.ttf +0 -0
- starplot/styles/fonts-library/inter/Inter-ThinItalic.ttf +0 -0
- starplot/styles/fonts-library/inter/LICENSE.txt +92 -0
- starplot/styles/fonts.py +15 -0
- starplot/styles/markers.py +207 -6
- starplot/utils.py +19 -0
- starplot/warnings.py +16 -0
- {starplot-0.12.5.dist-info → starplot-0.14.0.dist-info}/METADATA +12 -12
- starplot-0.14.0.dist-info/RECORD +107 -0
- starplot-0.12.5.dist-info/RECORD +0 -67
- {starplot-0.12.5.dist-info → starplot-0.14.0.dist-info}/LICENSE +0 -0
- {starplot-0.12.5.dist-info → starplot-0.14.0.dist-info}/WHEEL +0 -0
starplot/map.py
CHANGED
|
@@ -1,46 +1,67 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import math
|
|
3
|
-
import warnings
|
|
4
3
|
from typing import Callable
|
|
4
|
+
from functools import cache
|
|
5
5
|
|
|
6
6
|
from cartopy import crs as ccrs
|
|
7
7
|
from matplotlib import pyplot as plt
|
|
8
8
|
from matplotlib import path, patches, ticker
|
|
9
9
|
from matplotlib.ticker import FuncFormatter, FixedLocator
|
|
10
|
-
from shapely import
|
|
11
|
-
from shapely.ops import unary_union
|
|
10
|
+
from shapely import Polygon
|
|
12
11
|
from skyfield.api import Star as SkyfieldStar, wgs84
|
|
13
12
|
import geopandas as gpd
|
|
14
13
|
import numpy as np
|
|
15
14
|
|
|
15
|
+
from starplot.coordinates import CoordinateSystem
|
|
16
16
|
from starplot import geod
|
|
17
|
-
from starplot.base import BasePlot
|
|
18
|
-
from starplot.data import DataFiles, constellations as condata, stars
|
|
19
|
-
from starplot.data.constellations import CONSTELLATIONS_FULL_NAMES
|
|
17
|
+
from starplot.base import BasePlot, DPI
|
|
20
18
|
from starplot.mixins import ExtentMaskMixin
|
|
21
|
-
from starplot.
|
|
22
|
-
|
|
19
|
+
from starplot.plotters import (
|
|
20
|
+
ConstellationPlotterMixin,
|
|
21
|
+
StarPlotterMixin,
|
|
22
|
+
DsoPlotterMixin,
|
|
23
|
+
MilkyWayPlotterMixin,
|
|
24
|
+
)
|
|
23
25
|
from starplot.projections import Projection
|
|
24
26
|
from starplot.styles import (
|
|
25
27
|
ObjectStyle,
|
|
26
28
|
LabelStyle,
|
|
27
|
-
LineStyle,
|
|
28
29
|
PlotStyle,
|
|
29
|
-
PolygonStyle,
|
|
30
30
|
PathStyle,
|
|
31
|
-
extensions,
|
|
32
31
|
)
|
|
33
32
|
from starplot.styles.helpers import use_style
|
|
34
33
|
from starplot.utils import lon_to_ra, ra_to_lon
|
|
35
34
|
|
|
36
|
-
# Silence noisy cartopy warnings
|
|
37
|
-
warnings.filterwarnings("ignore", module="cartopy")
|
|
38
|
-
warnings.filterwarnings("ignore", module="shapely")
|
|
39
35
|
|
|
40
|
-
DEFAULT_MAP_STYLE = PlotStyle().extend(extensions.MAP)
|
|
36
|
+
DEFAULT_MAP_STYLE = PlotStyle() # .extend(extensions.MAP)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def points(start, end, num_points=100):
|
|
40
|
+
"""Generates points along a line segment.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
start (tuple): (x, y) coordinates of the starting point.
|
|
44
|
+
end (tuple): (x, y) coordinates of the ending point.
|
|
45
|
+
num_points (int): Number of points to generate.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
list: List of (x, y) coordinates of the generated points.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
x_coords = np.linspace(start[0], end[0], num_points)
|
|
52
|
+
y_coords = np.linspace(start[1], end[1], num_points)
|
|
41
53
|
|
|
54
|
+
return list(zip(x_coords, y_coords))
|
|
42
55
|
|
|
43
|
-
|
|
56
|
+
|
|
57
|
+
class MapPlot(
|
|
58
|
+
BasePlot,
|
|
59
|
+
ExtentMaskMixin,
|
|
60
|
+
StarPlotterMixin,
|
|
61
|
+
DsoPlotterMixin,
|
|
62
|
+
MilkyWayPlotterMixin,
|
|
63
|
+
ConstellationPlotterMixin,
|
|
64
|
+
):
|
|
44
65
|
"""Creates a new map plot.
|
|
45
66
|
|
|
46
67
|
!!! star "Note"
|
|
@@ -60,12 +81,17 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
60
81
|
resolution: Size (in pixels) of largest dimension of the map
|
|
61
82
|
hide_colliding_labels: If True, then labels will not be plotted if they collide with another existing label
|
|
62
83
|
clip_path: An optional Shapely Polygon that specifies the clip path of the plot -- only objects inside the polygon will be plotted. If `None` (the default), then the clip path will be the extent of the map you specified with the RA/DEC parameters.
|
|
84
|
+
scale: Scaling factor that will be applied to all sizes in styles (e.g. font size, marker size, line widths, etc). For example, if you want to make everything 2x bigger, then set the scale to 2. At `scale=1` and `resolution=4096` (the default), all sizes are optimized visually for a map that covers 1-3 constellations. So, if you're creating a plot of a _larger_ extent, then it'd probably be good to decrease the scale (i.e. make everything smaller) -- and _increase_ the scale if you're plotting a very small area.
|
|
85
|
+
autoscale: If True, then the scale will be set automatically based on resolution.
|
|
86
|
+
suppress_warnings: If True (the default), then all warnings will be suppressed
|
|
63
87
|
|
|
64
88
|
Returns:
|
|
65
89
|
MapPlot: A new instance of a MapPlot
|
|
66
90
|
|
|
67
91
|
"""
|
|
68
92
|
|
|
93
|
+
_coordinate_system = CoordinateSystem.RA_DEC
|
|
94
|
+
|
|
69
95
|
def __init__(
|
|
70
96
|
self,
|
|
71
97
|
projection: Projection,
|
|
@@ -78,9 +104,12 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
78
104
|
dt: datetime = None,
|
|
79
105
|
ephemeris: str = "de421_2001.bsp",
|
|
80
106
|
style: PlotStyle = DEFAULT_MAP_STYLE,
|
|
81
|
-
resolution: int =
|
|
107
|
+
resolution: int = 4096,
|
|
82
108
|
hide_colliding_labels: bool = True,
|
|
83
109
|
clip_path: Polygon = None,
|
|
110
|
+
scale: float = 1.0,
|
|
111
|
+
autoscale: bool = False,
|
|
112
|
+
suppress_warnings: bool = True,
|
|
84
113
|
*args,
|
|
85
114
|
**kwargs,
|
|
86
115
|
) -> "MapPlot":
|
|
@@ -90,6 +119,9 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
90
119
|
style,
|
|
91
120
|
resolution,
|
|
92
121
|
hide_colliding_labels,
|
|
122
|
+
scale=scale,
|
|
123
|
+
autoscale=autoscale,
|
|
124
|
+
suppress_warnings=suppress_warnings,
|
|
93
125
|
*args,
|
|
94
126
|
**kwargs,
|
|
95
127
|
)
|
|
@@ -143,6 +175,7 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
143
175
|
def _prepare_coords(self, ra: float, dec: float) -> (float, float):
|
|
144
176
|
return ra * 15, dec
|
|
145
177
|
|
|
178
|
+
@cache
|
|
146
179
|
def in_bounds(self, ra: float, dec: float) -> bool:
|
|
147
180
|
"""Determine if a coordinate is within the bounds of the plot.
|
|
148
181
|
|
|
@@ -235,246 +268,6 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
235
268
|
|
|
236
269
|
return df
|
|
237
270
|
|
|
238
|
-
@use_style(LineStyle, "constellation_borders")
|
|
239
|
-
def constellation_borders(self, style: LineStyle = None):
|
|
240
|
-
"""Plots the constellation borders
|
|
241
|
-
|
|
242
|
-
Args:
|
|
243
|
-
style: Styling of the constellation borders. If None, then the plot's style (specified when creating the plot) will be used
|
|
244
|
-
"""
|
|
245
|
-
constellation_borders = self._read_geo_package(
|
|
246
|
-
DataFiles.CONSTELLATION_BORDERS.value
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
if constellation_borders.empty:
|
|
250
|
-
return
|
|
251
|
-
|
|
252
|
-
style_kwargs = style.matplot_kwargs(self._size_multiplier)
|
|
253
|
-
|
|
254
|
-
geometries = []
|
|
255
|
-
|
|
256
|
-
for _, c in constellation_borders.iterrows():
|
|
257
|
-
for ls in c.geometry.geoms:
|
|
258
|
-
geometries.append(ls)
|
|
259
|
-
|
|
260
|
-
for ls in geometries:
|
|
261
|
-
x, y = ls.xy
|
|
262
|
-
self.ax.plot(
|
|
263
|
-
list(x),
|
|
264
|
-
list(y),
|
|
265
|
-
transform=self._plate_carree,
|
|
266
|
-
clip_on=True,
|
|
267
|
-
clip_path=self._background_clip_path,
|
|
268
|
-
**style_kwargs,
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
def _plot_constellation_borders(self):
|
|
272
|
-
"""work in progress"""
|
|
273
|
-
constellation_borders = gpd.read_file(
|
|
274
|
-
DataFiles.CONSTELLATIONS.value,
|
|
275
|
-
engine="pyogrio",
|
|
276
|
-
use_arrow=True,
|
|
277
|
-
bbox=self._extent_mask(),
|
|
278
|
-
)
|
|
279
|
-
|
|
280
|
-
if constellation_borders.empty:
|
|
281
|
-
return
|
|
282
|
-
|
|
283
|
-
geometries = []
|
|
284
|
-
|
|
285
|
-
for i, constellation in constellation_borders.iterrows():
|
|
286
|
-
geometry_types = constellation.geometry.geom_type
|
|
287
|
-
|
|
288
|
-
# equinox = LineString([[0, 90], [0, -90]])
|
|
289
|
-
"""
|
|
290
|
-
Problems:
|
|
291
|
-
- Need to handle multipolygon borders too (SER)
|
|
292
|
-
- Shapely's union doesn't handle geodesy (e.g. TRI + AND)
|
|
293
|
-
- ^^ TRI is plotted with ra < 360, but AND has ra > 360
|
|
294
|
-
- ^^ idea: create union first and then remove duplicate lines?
|
|
295
|
-
|
|
296
|
-
TODO: create new static data file of constellation border lines
|
|
297
|
-
"""
|
|
298
|
-
|
|
299
|
-
if "Polygon" in geometry_types and "MultiPolygon" not in geometry_types:
|
|
300
|
-
polygons = [constellation.geometry]
|
|
301
|
-
|
|
302
|
-
elif "MultiPolygon" in geometry_types:
|
|
303
|
-
polygons = constellation.geometry.geoms
|
|
304
|
-
|
|
305
|
-
for p in polygons:
|
|
306
|
-
coords = list(zip(*p.exterior.coords.xy))
|
|
307
|
-
# coords = [(ra * -1, dec) for ra, dec in coords]
|
|
308
|
-
|
|
309
|
-
new_coords = []
|
|
310
|
-
|
|
311
|
-
for i, c in enumerate(coords):
|
|
312
|
-
ra, dec = c
|
|
313
|
-
if i > 0:
|
|
314
|
-
if new_coords[i - 1][0] - ra > 60:
|
|
315
|
-
ra += 360
|
|
316
|
-
|
|
317
|
-
elif ra - new_coords[i - 1][0] > 60:
|
|
318
|
-
new_coords[i - 1][0] += 360
|
|
319
|
-
|
|
320
|
-
new_coords.append([ra, dec])
|
|
321
|
-
|
|
322
|
-
ls = LineString(new_coords)
|
|
323
|
-
geometries.append(ls)
|
|
324
|
-
|
|
325
|
-
mls = MultiLineString(geometries)
|
|
326
|
-
geometries = unary_union(mls)
|
|
327
|
-
|
|
328
|
-
style_kwargs = self.style.constellation_borders.matplot_kwargs(
|
|
329
|
-
size_multiplier=self._size_multiplier
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
for ls in list(geometries.geoms):
|
|
333
|
-
# print(ls)
|
|
334
|
-
x, y = ls.xy
|
|
335
|
-
newx = [xx * -1 for xx in list(x)]
|
|
336
|
-
self.ax.plot(
|
|
337
|
-
# list(x),
|
|
338
|
-
newx,
|
|
339
|
-
list(y),
|
|
340
|
-
# **self._plot_kwargs(),
|
|
341
|
-
# transform=self._geodetic,
|
|
342
|
-
transform=self._plate_carree,
|
|
343
|
-
**style_kwargs,
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
@use_style(PathStyle, "constellation")
|
|
347
|
-
def constellations(
|
|
348
|
-
self,
|
|
349
|
-
style: PathStyle = None,
|
|
350
|
-
labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
|
|
351
|
-
where: list = None,
|
|
352
|
-
):
|
|
353
|
-
"""Plots the constellation lines and/or labels
|
|
354
|
-
|
|
355
|
-
Args:
|
|
356
|
-
style: Styling of the constellations. If None, then the plot's style (specified when creating the plot) will be used
|
|
357
|
-
labels: A dictionary where the keys are each constellation's 3-letter abbreviation, and the values are how the constellation will be labeled on the plot.
|
|
358
|
-
where: A list of expressions that determine which constellations to plot. See [Selecting Objects](/reference-selecting-objects/) for details.
|
|
359
|
-
"""
|
|
360
|
-
self.logger.debug("Plotting constellations...")
|
|
361
|
-
|
|
362
|
-
labels = labels or {}
|
|
363
|
-
where = where or []
|
|
364
|
-
|
|
365
|
-
constellations_gdf = gpd.read_file(
|
|
366
|
-
DataFiles.CONSTELLATIONS.value,
|
|
367
|
-
engine="pyogrio",
|
|
368
|
-
use_arrow=True,
|
|
369
|
-
bbox=self._extent_mask(),
|
|
370
|
-
)
|
|
371
|
-
stars_df = stars.load("hipparcos")
|
|
372
|
-
|
|
373
|
-
if constellations_gdf.empty:
|
|
374
|
-
return
|
|
375
|
-
|
|
376
|
-
if self.projection in [Projection.MERCATOR, Projection.MILLER]:
|
|
377
|
-
transform = self._plate_carree
|
|
378
|
-
else:
|
|
379
|
-
transform = self._geodetic
|
|
380
|
-
|
|
381
|
-
conline_hips = condata.lines()
|
|
382
|
-
style_kwargs = style.line.matplot_kwargs(size_multiplier=self._size_multiplier)
|
|
383
|
-
|
|
384
|
-
for c in constellations_gdf.itertuples():
|
|
385
|
-
obj = constellation_from_tuple(c)
|
|
386
|
-
|
|
387
|
-
if not all([e.evaluate(obj) for e in where]):
|
|
388
|
-
continue
|
|
389
|
-
|
|
390
|
-
hiplines = conline_hips[c.iau_id]
|
|
391
|
-
inbounds = False
|
|
392
|
-
|
|
393
|
-
for s1_hip, s2_hip in hiplines:
|
|
394
|
-
s1 = stars_df.loc[s1_hip]
|
|
395
|
-
s2 = stars_df.loc[s2_hip]
|
|
396
|
-
|
|
397
|
-
s1_ra = s1.ra_hours * 15
|
|
398
|
-
s2_ra = s2.ra_hours * 15
|
|
399
|
-
|
|
400
|
-
s1_dec = s1.dec_degrees
|
|
401
|
-
s2_dec = s2.dec_degrees
|
|
402
|
-
|
|
403
|
-
if s1_ra - s2_ra > 60:
|
|
404
|
-
s2_ra += 360
|
|
405
|
-
|
|
406
|
-
elif s2_ra - s1_ra > 60:
|
|
407
|
-
s1_ra += 360
|
|
408
|
-
|
|
409
|
-
if self.in_bounds(s1_ra / 15, s1_dec):
|
|
410
|
-
inbounds = True
|
|
411
|
-
|
|
412
|
-
s1_ra *= -1
|
|
413
|
-
s2_ra *= -1
|
|
414
|
-
|
|
415
|
-
# make lines straight
|
|
416
|
-
# s1_ra, s1_dec = self._proj.transform_point(s1_ra, s1.dec_degrees, self._geodetic)
|
|
417
|
-
# s2_ra, s2_dec = self._proj.transform_point(s2_ra, s2.dec_degrees, self._geodetic)
|
|
418
|
-
|
|
419
|
-
self.ax.plot(
|
|
420
|
-
[s1_ra, s2_ra],
|
|
421
|
-
[s1_dec, s2_dec],
|
|
422
|
-
transform=transform,
|
|
423
|
-
**style_kwargs,
|
|
424
|
-
clip_on=True,
|
|
425
|
-
clip_path=self._background_clip_path,
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
if inbounds:
|
|
429
|
-
self._objects.constellations.append(obj)
|
|
430
|
-
|
|
431
|
-
self._plot_constellation_labels(style.label, labels)
|
|
432
|
-
|
|
433
|
-
def _plot_constellation_labels(
|
|
434
|
-
self,
|
|
435
|
-
style: PathStyle = None,
|
|
436
|
-
labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
|
|
437
|
-
):
|
|
438
|
-
style = style or self.style.constellation.label
|
|
439
|
-
|
|
440
|
-
for con in condata.iterator():
|
|
441
|
-
_, ra, dec = condata.get(con)
|
|
442
|
-
text = labels.get(con.lower())
|
|
443
|
-
self.text(text, ra, dec, style)
|
|
444
|
-
|
|
445
|
-
@use_style(PolygonStyle, "milky_way")
|
|
446
|
-
def milky_way(self, style: PolygonStyle = None):
|
|
447
|
-
"""Plots the Milky Way
|
|
448
|
-
|
|
449
|
-
Args:
|
|
450
|
-
style: Styling of the Milky Way. If None, then the plot's style (specified when creating the plot) will be used
|
|
451
|
-
"""
|
|
452
|
-
mw = self._read_geo_package(DataFiles.MILKY_WAY.value)
|
|
453
|
-
|
|
454
|
-
if mw.empty:
|
|
455
|
-
return
|
|
456
|
-
|
|
457
|
-
def _prepare_polygon(p):
|
|
458
|
-
points = list(zip(*p.boundary.coords.xy))
|
|
459
|
-
# convert lon to RA and reverse so the coordinates are counterclockwise order
|
|
460
|
-
return [(lon_to_ra(lon) * 15, dec) for lon, dec in reversed(points)]
|
|
461
|
-
|
|
462
|
-
# create union of all Milky Way patches
|
|
463
|
-
gs = mw.geometry.to_crs(self._plate_carree)
|
|
464
|
-
mw_union = gs.buffer(0.1).unary_union.buffer(-0.1)
|
|
465
|
-
polygons = []
|
|
466
|
-
|
|
467
|
-
if mw_union.geom_type == "MultiPolygon":
|
|
468
|
-
polygons.extend([_prepare_polygon(polygon) for polygon in mw_union.geoms])
|
|
469
|
-
else:
|
|
470
|
-
polygons.append(_prepare_polygon(mw_union))
|
|
471
|
-
|
|
472
|
-
for polygon_points in polygons:
|
|
473
|
-
self._polygon(
|
|
474
|
-
polygon_points,
|
|
475
|
-
style=style,
|
|
476
|
-
)
|
|
477
|
-
|
|
478
271
|
@use_style(ObjectStyle, "zenith")
|
|
479
272
|
def zenith(
|
|
480
273
|
self,
|
|
@@ -535,37 +328,51 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
535
328
|
)
|
|
536
329
|
x = []
|
|
537
330
|
y = []
|
|
538
|
-
verts = []
|
|
539
|
-
|
|
540
|
-
# TODO : handle map edges better
|
|
541
331
|
|
|
542
332
|
for ra, dec in points:
|
|
543
333
|
ra = ra / 15
|
|
544
334
|
x0, y0 = self._prepare_coords(ra, dec)
|
|
545
335
|
x.append(x0)
|
|
546
336
|
y.append(y0)
|
|
547
|
-
verts.append((x0, y0))
|
|
548
337
|
|
|
549
338
|
style_kwargs = {}
|
|
550
339
|
if self.projection == Projection.ZENITH:
|
|
551
340
|
"""
|
|
552
|
-
For zenith projections, we plot the horizon as a patch
|
|
553
|
-
plottting as a line results in extra pixels on bottom.
|
|
554
|
-
|
|
555
|
-
TODO : investigate why line is extra thick on bottom when plotting line
|
|
341
|
+
For zenith projections, we plot the horizon as a patch to make a more perfect circle
|
|
556
342
|
"""
|
|
557
|
-
style_kwargs = style.line.matplot_kwargs(self.
|
|
343
|
+
style_kwargs = style.line.matplot_kwargs(self.scale)
|
|
558
344
|
style_kwargs["clip_on"] = False
|
|
559
345
|
style_kwargs["edgecolor"] = style_kwargs.pop("color")
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
346
|
+
patch = patches.Circle(
|
|
347
|
+
(0.50, 0.50),
|
|
348
|
+
radius=0.454,
|
|
563
349
|
facecolor=None,
|
|
564
350
|
fill=False,
|
|
565
|
-
transform=self.
|
|
351
|
+
transform=self.ax.transAxes,
|
|
566
352
|
**style_kwargs,
|
|
567
353
|
)
|
|
568
354
|
self.ax.add_patch(patch)
|
|
355
|
+
self._background_clip_path = patch
|
|
356
|
+
|
|
357
|
+
if not labels:
|
|
358
|
+
return
|
|
359
|
+
|
|
360
|
+
label_ax_coords = [
|
|
361
|
+
(0.5, 0.95), # north
|
|
362
|
+
(0.045, 0.5), # east
|
|
363
|
+
(0.5, 0.045), # south
|
|
364
|
+
(0.954, 0.5), # west
|
|
365
|
+
]
|
|
366
|
+
for label, coords in zip(labels, label_ax_coords):
|
|
367
|
+
self.ax.annotate(
|
|
368
|
+
label,
|
|
369
|
+
coords,
|
|
370
|
+
xycoords=self.ax.transAxes,
|
|
371
|
+
clip_on=False,
|
|
372
|
+
**style.label.matplot_kwargs(self.scale),
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
return
|
|
569
376
|
|
|
570
377
|
else:
|
|
571
378
|
style_kwargs["clip_on"] = True
|
|
@@ -574,18 +381,11 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
574
381
|
x,
|
|
575
382
|
y,
|
|
576
383
|
dash_capstyle=style.line.dash_capstyle,
|
|
577
|
-
**style.line.matplot_kwargs(self.
|
|
384
|
+
**style.line.matplot_kwargs(self.scale),
|
|
578
385
|
**style_kwargs,
|
|
579
386
|
**self._plot_kwargs(),
|
|
580
387
|
)
|
|
581
388
|
|
|
582
|
-
# self.circle(
|
|
583
|
-
# (ra.hours, dec.degrees),
|
|
584
|
-
# 90,
|
|
585
|
-
# style,
|
|
586
|
-
# num_pts=200,
|
|
587
|
-
# )
|
|
588
|
-
|
|
589
389
|
if not labels:
|
|
590
390
|
return
|
|
591
391
|
|
|
@@ -597,10 +397,13 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
597
397
|
cardinal_directions = [north, east, south, west]
|
|
598
398
|
|
|
599
399
|
text_kwargs = dict(
|
|
600
|
-
**style.label.matplot_kwargs(self.
|
|
400
|
+
**style.label.matplot_kwargs(self.scale),
|
|
601
401
|
hide_on_collision=False,
|
|
602
|
-
xytext=(
|
|
603
|
-
|
|
402
|
+
xytext=(
|
|
403
|
+
style.label.offset_x * self.scale,
|
|
404
|
+
style.label.offset_y * self.scale,
|
|
405
|
+
),
|
|
406
|
+
textcoords="offset points",
|
|
604
407
|
path_effects=[],
|
|
605
408
|
)
|
|
606
409
|
|
|
@@ -609,7 +412,8 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
609
412
|
|
|
610
413
|
for i, position in enumerate(cardinal_directions):
|
|
611
414
|
ra, dec, _ = position.radec()
|
|
612
|
-
self.
|
|
415
|
+
x, y = self._prepare_coords(ra, dec)
|
|
416
|
+
self._text(x, y, labels[i], **text_kwargs)
|
|
613
417
|
|
|
614
418
|
@use_style(PathStyle, "gridlines")
|
|
615
419
|
def gridlines(
|
|
@@ -664,6 +468,7 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
664
468
|
ypadding=12,
|
|
665
469
|
clip_on=True,
|
|
666
470
|
clip_path=self._background_clip_path,
|
|
471
|
+
gid="gridlines",
|
|
667
472
|
**line_style_kwargs,
|
|
668
473
|
)
|
|
669
474
|
|
|
@@ -681,6 +486,7 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
681
486
|
self.ax.plot(
|
|
682
487
|
(ra * 15, ra * 15),
|
|
683
488
|
(-90, 90),
|
|
489
|
+
gid="gridlines",
|
|
684
490
|
**line_style_kwargs,
|
|
685
491
|
**self._plot_kwargs(),
|
|
686
492
|
)
|
|
@@ -732,6 +538,7 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
732
538
|
figsize=(self.figure_size, self.figure_size),
|
|
733
539
|
facecolor=self.style.figure_background_color.as_hex(),
|
|
734
540
|
layout="constrained",
|
|
541
|
+
dpi=DPI,
|
|
735
542
|
)
|
|
736
543
|
bounds = self._latlon_bounds()
|
|
737
544
|
center_lat = (bounds[2] + bounds[3]) / 2
|
|
@@ -800,9 +607,15 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
800
607
|
0.05,
|
|
801
608
|
info,
|
|
802
609
|
transform=self.ax.transAxes,
|
|
803
|
-
**style.matplot_kwargs(self.
|
|
610
|
+
**style.matplot_kwargs(self.scale),
|
|
804
611
|
)
|
|
805
612
|
|
|
613
|
+
def _ax_to_radec(self, x, y):
|
|
614
|
+
trans = self.ax.transAxes + self.ax.transData.inverted()
|
|
615
|
+
x_projected, y_projected = trans.transform((x, y)) # axes to data
|
|
616
|
+
x_ra, y_ra = self._crs.transform_point(x_projected, y_projected, self._proj)
|
|
617
|
+
return (x_ra + 360) / 15, y_ra
|
|
618
|
+
|
|
806
619
|
def _plot_background_clip_path(self):
|
|
807
620
|
def to_axes(points):
|
|
808
621
|
ax_points = []
|
|
@@ -830,7 +643,7 @@ class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
|
|
|
830
643
|
fill=True,
|
|
831
644
|
facecolor=self.style.background_color.as_hex(),
|
|
832
645
|
# edgecolor=self.style.border_line_color.as_hex(),
|
|
833
|
-
linewidth=0,
|
|
646
|
+
linewidth=0,
|
|
834
647
|
zorder=-2_000,
|
|
835
648
|
transform=self.ax.transAxes,
|
|
836
649
|
)
|
starplot/models/base.py
CHANGED
|
@@ -113,6 +113,8 @@ class SkyObject(CreateMapMixin, CreateOpticMixin, metaclass=Meta):
|
|
|
113
113
|
dec: float
|
|
114
114
|
"""Declination, in degrees (-90...90)"""
|
|
115
115
|
|
|
116
|
+
_constellation_id = None
|
|
117
|
+
|
|
116
118
|
constellation_id: Optional[str] = None
|
|
117
119
|
"""Identifier of the constellation that contains this object. The ID is the three-letter (all lowercase) abbreviation from the International Astronomical Union (IAU)."""
|
|
118
120
|
|
|
@@ -120,8 +122,13 @@ class SkyObject(CreateMapMixin, CreateOpticMixin, metaclass=Meta):
|
|
|
120
122
|
self.ra = ra
|
|
121
123
|
self.dec = dec
|
|
122
124
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
@property
|
|
126
|
+
def constellation_id(self):
|
|
127
|
+
"""Identifier of the constellation that contains this object. The ID is the three-letter (all lowercase) abbreviation from the International Astronomical Union (IAU)."""
|
|
128
|
+
if not self._constellation_id:
|
|
129
|
+
pos = position_of_radec(self.ra, self.dec)
|
|
130
|
+
self._constellation_id = constellation_at(pos).lower()
|
|
131
|
+
return self._constellation_id
|
|
125
132
|
|
|
126
133
|
def constellation(self):
|
|
127
134
|
"""Returns an instance of the [`Constellation`][starplot.models.Constellation] that contains this object"""
|
starplot/models/constellation.py
CHANGED
|
@@ -39,7 +39,7 @@ class Constellation(SkyObject):
|
|
|
39
39
|
) -> None:
|
|
40
40
|
super().__init__(ra, dec)
|
|
41
41
|
self.iau_id = iau_id.lower()
|
|
42
|
-
self.
|
|
42
|
+
self._constellation_id = self.iau_id # override from super()
|
|
43
43
|
self.name = name
|
|
44
44
|
self.boundary = boundary
|
|
45
45
|
|