starplot 0.20.5__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 +59 -0
- starplot/callables.py +176 -0
- starplot/cli.py +29 -0
- starplot/config.py +80 -0
- starplot/coordinates.py +7 -0
- starplot/data/__init__.py +22 -0
- starplot/data/catalogs.py +356 -0
- starplot/data/constellations.py +71 -0
- starplot/data/db.py +33 -0
- starplot/data/dsos.py +85 -0
- starplot/data/ecliptic.py +201 -0
- starplot/data/library/constellation_names.parquet +0 -0
- starplot/data/library/dso_names.parquet +0 -0
- starplot/data/library/readme.md +1 -0
- starplot/data/library/star_designations.parquet +0 -0
- starplot/data/stars.py +79 -0
- starplot/data/translations.py +408 -0
- starplot/data/utils.py +35 -0
- starplot/geometry.py +319 -0
- starplot/mixins.py +360 -0
- starplot/models/__init__.py +21 -0
- starplot/models/base.py +77 -0
- starplot/models/comet.py +302 -0
- starplot/models/constellation.py +151 -0
- starplot/models/dso.py +317 -0
- starplot/models/milky_way.py +30 -0
- starplot/models/moon.py +130 -0
- starplot/models/objects.py +29 -0
- starplot/models/observer.py +125 -0
- starplot/models/optics.py +342 -0
- starplot/models/planet.py +137 -0
- starplot/models/satellite.py +138 -0
- starplot/models/star.py +253 -0
- starplot/models/sun.py +62 -0
- starplot/plots/__init__.py +7 -0
- starplot/plots/base.py +1023 -0
- starplot/plots/galaxy.py +372 -0
- starplot/plots/horizon.py +543 -0
- starplot/plots/map.py +515 -0
- starplot/plots/optic.py +469 -0
- starplot/plots/zenith.py +217 -0
- starplot/plotters/__init__.py +9 -0
- starplot/plotters/arrow.py +174 -0
- starplot/plotters/constellations.py +298 -0
- starplot/plotters/debug.py +21 -0
- starplot/plotters/dsos.py +294 -0
- starplot/plotters/experimental.py +722 -0
- starplot/plotters/gradients.py +153 -0
- starplot/plotters/legend.py +253 -0
- starplot/plotters/milkyway.py +51 -0
- starplot/plotters/stars.py +319 -0
- starplot/plotters/text.py +802 -0
- starplot/profile.py +16 -0
- starplot/projections.py +184 -0
- starplot/styles/__init__.py +6 -0
- starplot/styles/base.py +1344 -0
- starplot/styles/ext/antique.yml +175 -0
- starplot/styles/ext/blue_dark.yml +163 -0
- starplot/styles/ext/blue_gold.yml +147 -0
- starplot/styles/ext/blue_light.yml +123 -0
- starplot/styles/ext/blue_medium.yml +142 -0
- starplot/styles/ext/blue_night.yml +185 -0
- starplot/styles/ext/cb_wong.yml +124 -0
- starplot/styles/ext/color_print.yml +111 -0
- starplot/styles/ext/gradient_presets.yml +158 -0
- starplot/styles/ext/grayscale.yml +94 -0
- starplot/styles/ext/grayscale_dark.yml +136 -0
- starplot/styles/ext/map.yml +12 -0
- starplot/styles/ext/nord.yml +158 -0
- starplot/styles/ext/optic.yml +20 -0
- starplot/styles/ext/publication.yml +8 -0
- starplot/styles/extensions.py +129 -0
- 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/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-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-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/LICENSE.txt +92 -0
- starplot/styles/fonts.py +15 -0
- starplot/styles/helpers.py +93 -0
- starplot/styles/markers.py +308 -0
- starplot/utils.py +169 -0
- starplot/warnings.py +21 -0
- starplot-0.20.5.dist-info/METADATA +146 -0
- starplot-0.20.5.dist-info/RECORD +98 -0
- starplot-0.20.5.dist-info/WHEEL +4 -0
- starplot-0.20.5.dist-info/entry_points.txt +3 -0
- starplot-0.20.5.dist-info/licenses/LICENSE +21 -0
starplot/styles/base.py
ADDED
|
@@ -0,0 +1,1344 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional, Union
|
|
6
|
+
|
|
7
|
+
import yaml
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
from pydantic.color import Color
|
|
11
|
+
from pydantic.functional_serializers import PlainSerializer
|
|
12
|
+
from matplotlib import patheffects
|
|
13
|
+
from typing_extensions import Annotated
|
|
14
|
+
|
|
15
|
+
from starplot.models.dso import DsoType
|
|
16
|
+
from starplot.styles.helpers import merge_dict
|
|
17
|
+
from starplot.styles.markers import (
|
|
18
|
+
ellipse,
|
|
19
|
+
circle_cross,
|
|
20
|
+
circle_crosshair,
|
|
21
|
+
circle_line,
|
|
22
|
+
circle_dot,
|
|
23
|
+
circle_dotted_rings,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
ColorStr = Annotated[
|
|
28
|
+
Color,
|
|
29
|
+
PlainSerializer(
|
|
30
|
+
lambda c: c.as_hex() if c and c != "none" else None,
|
|
31
|
+
return_type=str,
|
|
32
|
+
),
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
HERE = Path(__file__).resolve().parent
|
|
37
|
+
|
|
38
|
+
PI = 3.141592653589793
|
|
39
|
+
SQR_2 = 1.41421356237
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class BaseStyle(BaseModel):
|
|
43
|
+
__hash__ = object.__hash__
|
|
44
|
+
|
|
45
|
+
class Config:
|
|
46
|
+
extra = "forbid"
|
|
47
|
+
use_enum_values = True
|
|
48
|
+
validate_assignment = True
|
|
49
|
+
|
|
50
|
+
def __enter__(self):
|
|
51
|
+
self._original = self.model_copy(deep=True)
|
|
52
|
+
return self
|
|
53
|
+
|
|
54
|
+
def __exit__(self, exception_type, exception_value, traceback):
|
|
55
|
+
for field_name in self.__pydantic_fields__.keys():
|
|
56
|
+
original_value = getattr(self._original, field_name)
|
|
57
|
+
setattr(self, field_name, original_value)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class GradientDirection(str, Enum):
|
|
61
|
+
LINEAR = "linear"
|
|
62
|
+
RADIAL = "radial"
|
|
63
|
+
MOLLWEIDE = "mollweide"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class FillStyleEnum(str, Enum):
|
|
67
|
+
"""Constants that represent the possible fill styles for markers."""
|
|
68
|
+
|
|
69
|
+
FULL = "full"
|
|
70
|
+
"""Fill the marker completely"""
|
|
71
|
+
|
|
72
|
+
LEFT = "left"
|
|
73
|
+
"""Fill the left half of the marker"""
|
|
74
|
+
|
|
75
|
+
RIGHT = "right"
|
|
76
|
+
"""Fill the right half of the marker"""
|
|
77
|
+
|
|
78
|
+
BOTTOM = "bottom"
|
|
79
|
+
"""Fill the bottom half"""
|
|
80
|
+
|
|
81
|
+
TOP = "top"
|
|
82
|
+
"""Fill the top half"""
|
|
83
|
+
|
|
84
|
+
NONE = "none"
|
|
85
|
+
"""Do not fill the marker. It'll still have an edge, but the inside will be transparent."""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class FontWeightEnum(int, Enum):
|
|
89
|
+
"""Options for font weight."""
|
|
90
|
+
|
|
91
|
+
THIN = 100
|
|
92
|
+
EXTRA_LIGHT = 200
|
|
93
|
+
# LIGHT = 300 # matplotlib's font dict doesn't have 300?
|
|
94
|
+
NORMAL = 400
|
|
95
|
+
MEDIUM = 500
|
|
96
|
+
SEMI_BOLD = 600
|
|
97
|
+
BOLD = 700
|
|
98
|
+
EXTRA_BOLD = 800
|
|
99
|
+
HEAVY = 900
|
|
100
|
+
|
|
101
|
+
def as_matplot(self) -> str:
|
|
102
|
+
"""Returns the font weight as a matplotlib string, which avoids a bug with integer font weights and rendering text as elements in SVG."""
|
|
103
|
+
return {
|
|
104
|
+
FontWeightEnum.THIN: "ultralight",
|
|
105
|
+
FontWeightEnum.EXTRA_LIGHT: "light", # matplotlib maps 'light' to 200, which is really extra light
|
|
106
|
+
# FontWeightEnum.LIGHT: "light",
|
|
107
|
+
FontWeightEnum.NORMAL: "normal",
|
|
108
|
+
FontWeightEnum.MEDIUM: "medium",
|
|
109
|
+
FontWeightEnum.SEMI_BOLD: "semibold",
|
|
110
|
+
FontWeightEnum.BOLD: "bold",
|
|
111
|
+
FontWeightEnum.EXTRA_BOLD: "extra bold",
|
|
112
|
+
FontWeightEnum.HEAVY: "black",
|
|
113
|
+
}[self.value]
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class FontStyleEnum(str, Enum):
|
|
117
|
+
NORMAL = "normal"
|
|
118
|
+
ITALIC = "italic"
|
|
119
|
+
OBLIQUE = "oblique"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class MarkerSymbolEnum(str, Enum):
|
|
123
|
+
"""Options for marker symbols"""
|
|
124
|
+
|
|
125
|
+
POINT = "point"
|
|
126
|
+
"""\u00B7"""
|
|
127
|
+
|
|
128
|
+
PLUS = "plus"
|
|
129
|
+
"""+"""
|
|
130
|
+
|
|
131
|
+
CIRCLE = "circle"
|
|
132
|
+
"""\u25CF"""
|
|
133
|
+
|
|
134
|
+
SQUARE = "square"
|
|
135
|
+
"""\u25A0"""
|
|
136
|
+
|
|
137
|
+
SQUARE_STRIPES_DIAGONAL = "square_stripes_diagonal"
|
|
138
|
+
"""\u25A8"""
|
|
139
|
+
|
|
140
|
+
STAR = "star"
|
|
141
|
+
"""\u2605"""
|
|
142
|
+
|
|
143
|
+
SUN = "sun"
|
|
144
|
+
"""\u263C"""
|
|
145
|
+
|
|
146
|
+
DIAMOND = "diamond"
|
|
147
|
+
"""\u25C6"""
|
|
148
|
+
|
|
149
|
+
TRIANGLE = "triangle"
|
|
150
|
+
"""\u23F6"""
|
|
151
|
+
|
|
152
|
+
CIRCLE_PLUS = "circle_plus"
|
|
153
|
+
"""\u2295"""
|
|
154
|
+
|
|
155
|
+
CIRCLE_CROSS = "circle_cross"
|
|
156
|
+
"""\u1AA0"""
|
|
157
|
+
|
|
158
|
+
CIRCLE_CROSSHAIR = "circle_crosshair"
|
|
159
|
+
"""No preview available, but this is the standard symbol for planetary nebulae"""
|
|
160
|
+
|
|
161
|
+
CIRCLE_DOT = "circle_dot"
|
|
162
|
+
"""\u29BF"""
|
|
163
|
+
|
|
164
|
+
CIRCLE_DOTTED_EDGE = "circle_dotted_edge"
|
|
165
|
+
"""\u25CC"""
|
|
166
|
+
|
|
167
|
+
CIRCLE_DOTTED_RINGS = "circle_dotted_rings"
|
|
168
|
+
|
|
169
|
+
CIRCLE_LINE = "circle_line"
|
|
170
|
+
"""\u29B5 the standard symbol for double stars"""
|
|
171
|
+
|
|
172
|
+
COMET = "comet"
|
|
173
|
+
"""\u2604"""
|
|
174
|
+
|
|
175
|
+
STAR_4 = "star_4"
|
|
176
|
+
"""\u2726"""
|
|
177
|
+
|
|
178
|
+
STAR_8 = "star_8"
|
|
179
|
+
"""\u2734"""
|
|
180
|
+
|
|
181
|
+
ELLIPSE = "ellipse"
|
|
182
|
+
"""\u2B2D"""
|
|
183
|
+
|
|
184
|
+
def as_matplot(self) -> str:
|
|
185
|
+
"""Returns the matplotlib value of this marker"""
|
|
186
|
+
return {
|
|
187
|
+
MarkerSymbolEnum.POINT: ".",
|
|
188
|
+
MarkerSymbolEnum.CIRCLE: "o",
|
|
189
|
+
MarkerSymbolEnum.SQUARE: "s",
|
|
190
|
+
MarkerSymbolEnum.PLUS: "P",
|
|
191
|
+
MarkerSymbolEnum.SQUARE_STRIPES_DIAGONAL: "$\u25A8$",
|
|
192
|
+
MarkerSymbolEnum.STAR: "*",
|
|
193
|
+
MarkerSymbolEnum.SUN: "$\u263C$",
|
|
194
|
+
MarkerSymbolEnum.DIAMOND: "D",
|
|
195
|
+
MarkerSymbolEnum.TRIANGLE: "^",
|
|
196
|
+
MarkerSymbolEnum.CIRCLE_PLUS: "$\u2295$",
|
|
197
|
+
MarkerSymbolEnum.CIRCLE_CROSS: circle_cross(),
|
|
198
|
+
MarkerSymbolEnum.CIRCLE_CROSSHAIR: circle_crosshair(),
|
|
199
|
+
MarkerSymbolEnum.CIRCLE_DOT: circle_dot(),
|
|
200
|
+
MarkerSymbolEnum.CIRCLE_DOTTED_EDGE: "$\u25CC$",
|
|
201
|
+
MarkerSymbolEnum.CIRCLE_DOTTED_RINGS: circle_dotted_rings(),
|
|
202
|
+
MarkerSymbolEnum.CIRCLE_LINE: circle_line(),
|
|
203
|
+
MarkerSymbolEnum.COMET: "$\u2604$",
|
|
204
|
+
MarkerSymbolEnum.STAR_4: "$\u2726$",
|
|
205
|
+
MarkerSymbolEnum.STAR_8: "$\u2734$",
|
|
206
|
+
MarkerSymbolEnum.ELLIPSE: ellipse(),
|
|
207
|
+
}[self.value]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class LineStyleEnum(str, Enum):
|
|
211
|
+
SOLID = "solid"
|
|
212
|
+
DASHED = "dashed"
|
|
213
|
+
DASHED_DOTS = "dashdot"
|
|
214
|
+
DOTTED = "dotted"
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class CapStyleEnum(str, Enum):
|
|
218
|
+
BUTT = "butt"
|
|
219
|
+
PROJECTING = "projecting"
|
|
220
|
+
ROUND = "round"
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class JoinStyleEnum(str, Enum):
|
|
224
|
+
MITRE = "mitre"
|
|
225
|
+
BEVEL = "bevel"
|
|
226
|
+
ROUND = "round"
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class LegendLocationEnum(str, Enum):
|
|
230
|
+
"""Options for the location of the map legend"""
|
|
231
|
+
|
|
232
|
+
INSIDE_TOP = "upper center"
|
|
233
|
+
INSIDE_TOP_LEFT = "upper left"
|
|
234
|
+
INSIDE_TOP_RIGHT = "upper right"
|
|
235
|
+
INSIDE_BOTTOM = "lower center"
|
|
236
|
+
INSIDE_BOTTOM_RIGHT = "lower right"
|
|
237
|
+
INSIDE_BOTTOM_LEFT = "lower left"
|
|
238
|
+
|
|
239
|
+
# OUTSIDE_TOP = "outside upper center"
|
|
240
|
+
# OUTSIDE_BOTTOM = "outside lower center"
|
|
241
|
+
|
|
242
|
+
OUTSIDE_TOP_LEFT = "outside left upper"
|
|
243
|
+
OUTSIDE_TOP_RIGHT = "outside right upper"
|
|
244
|
+
OUTSIDE_BOTTOM_RIGHT = "outside right lower"
|
|
245
|
+
OUTSIDE_BOTTOM_LEFT = "outside left lower"
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class AnchorPointEnum(str, Enum):
|
|
249
|
+
"""Options for the anchor point of labels"""
|
|
250
|
+
|
|
251
|
+
CENTER = "center"
|
|
252
|
+
LEFT_CENTER = "left center"
|
|
253
|
+
RIGHT_CENTER = "right center"
|
|
254
|
+
TOP_LEFT = "top left"
|
|
255
|
+
TOP_RIGHT = "top right"
|
|
256
|
+
TOP_CENTER = "top center"
|
|
257
|
+
BOTTOM_LEFT = "bottom left"
|
|
258
|
+
BOTTOM_RIGHT = "bottom right"
|
|
259
|
+
BOTTOM_CENTER = "bottom center"
|
|
260
|
+
|
|
261
|
+
def as_matplot(self) -> dict:
|
|
262
|
+
style = {}
|
|
263
|
+
# the values below look wrong, but they're inverted because the map coords are inverted
|
|
264
|
+
if self.value == AnchorPointEnum.BOTTOM_LEFT:
|
|
265
|
+
style["va"] = "top"
|
|
266
|
+
style["ha"] = "right"
|
|
267
|
+
elif self.value == AnchorPointEnum.BOTTOM_RIGHT:
|
|
268
|
+
style["va"] = "top"
|
|
269
|
+
style["ha"] = "left"
|
|
270
|
+
elif self.value == AnchorPointEnum.BOTTOM_CENTER:
|
|
271
|
+
style["va"] = "top"
|
|
272
|
+
style["ha"] = "center"
|
|
273
|
+
elif self.value == AnchorPointEnum.TOP_LEFT:
|
|
274
|
+
style["va"] = "bottom"
|
|
275
|
+
style["ha"] = "right"
|
|
276
|
+
elif self.value == AnchorPointEnum.TOP_RIGHT:
|
|
277
|
+
style["va"] = "bottom"
|
|
278
|
+
style["ha"] = "left"
|
|
279
|
+
elif self.value == AnchorPointEnum.TOP_CENTER:
|
|
280
|
+
style["va"] = "bottom"
|
|
281
|
+
style["ha"] = "center"
|
|
282
|
+
elif self.value == AnchorPointEnum.CENTER:
|
|
283
|
+
style["va"] = "center"
|
|
284
|
+
style["ha"] = "center"
|
|
285
|
+
elif self.value == AnchorPointEnum.LEFT_CENTER:
|
|
286
|
+
style["va"] = "center"
|
|
287
|
+
style["ha"] = "right"
|
|
288
|
+
elif self.value == AnchorPointEnum.RIGHT_CENTER:
|
|
289
|
+
style["va"] = "center"
|
|
290
|
+
style["ha"] = "left"
|
|
291
|
+
|
|
292
|
+
return style
|
|
293
|
+
|
|
294
|
+
@staticmethod
|
|
295
|
+
def from_str(value: str) -> "AnchorPointEnum":
|
|
296
|
+
options = {ap.value: ap for ap in AnchorPointEnum}
|
|
297
|
+
return options.get(value)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class AlignmentEnum(str, Enum):
|
|
301
|
+
"""Alignment options for the legend's title and entries"""
|
|
302
|
+
|
|
303
|
+
LEFT = "left"
|
|
304
|
+
RIGHT = "right"
|
|
305
|
+
CENTER = "center"
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class ZOrderEnum(int, Enum):
|
|
309
|
+
"""
|
|
310
|
+
Z Order presets for managing layers
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
LAYER_1 = -2_000
|
|
314
|
+
"""Bottom layer"""
|
|
315
|
+
|
|
316
|
+
LAYER_2 = -1_000
|
|
317
|
+
|
|
318
|
+
LAYER_3 = 0
|
|
319
|
+
"""Middle layer"""
|
|
320
|
+
|
|
321
|
+
LAYER_4 = 1_000
|
|
322
|
+
|
|
323
|
+
LAYER_5 = 2_000
|
|
324
|
+
"""Top layer"""
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class MarkerStyle(BaseStyle):
|
|
328
|
+
"""
|
|
329
|
+
Styling properties for markers.
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
color: Optional[ColorStr] = ColorStr("#000")
|
|
333
|
+
"""Fill color of marker. Can be a hex, rgb, hsl, or word string."""
|
|
334
|
+
|
|
335
|
+
edge_color: Optional[ColorStr] = ColorStr("#000")
|
|
336
|
+
"""Edge color of marker. Can be a hex, rgb, hsl, or word string."""
|
|
337
|
+
|
|
338
|
+
edge_width: float = 1
|
|
339
|
+
"""Edge width of marker, in points. Not available for all marker symbols."""
|
|
340
|
+
|
|
341
|
+
line_style: Union[LineStyleEnum, tuple] = LineStyleEnum.SOLID
|
|
342
|
+
"""Edge line style. Can be a predefined value in `LineStyleEnum` or a [Matplotlib linestyle tuple](https://matplotlib.org/stable/gallery/lines_bars_and_markers/linestyles.html)."""
|
|
343
|
+
|
|
344
|
+
dash_capstyle: CapStyleEnum = CapStyleEnum.PROJECTING
|
|
345
|
+
"""Style of dash endpoints"""
|
|
346
|
+
|
|
347
|
+
symbol: MarkerSymbolEnum = MarkerSymbolEnum.POINT
|
|
348
|
+
"""Symbol for marker"""
|
|
349
|
+
|
|
350
|
+
size: float = 22
|
|
351
|
+
"""Size of marker in points"""
|
|
352
|
+
|
|
353
|
+
fill: FillStyleEnum = FillStyleEnum.NONE
|
|
354
|
+
"""Fill style of marker"""
|
|
355
|
+
|
|
356
|
+
alpha: float = 1.0
|
|
357
|
+
"""Alpha value (controls transparency)"""
|
|
358
|
+
|
|
359
|
+
zorder: int = ZOrderEnum.LAYER_2
|
|
360
|
+
"""Zorder of marker"""
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def symbol_matplot(self) -> str:
|
|
364
|
+
return MarkerSymbolEnum(self.symbol).as_matplot()
|
|
365
|
+
|
|
366
|
+
def matplot_kwargs(self, scale: float = 1.0) -> dict:
|
|
367
|
+
return dict(
|
|
368
|
+
color=self.color.as_hex() if self.color else "none",
|
|
369
|
+
markeredgecolor=self.edge_color.as_hex() if self.edge_color else "none",
|
|
370
|
+
marker=MarkerSymbolEnum(self.symbol).as_matplot(),
|
|
371
|
+
markersize=self.size * scale,
|
|
372
|
+
fillstyle=self.fill,
|
|
373
|
+
alpha=self.alpha,
|
|
374
|
+
zorder=self.zorder,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
def matplot_scatter_kwargs(self, scale: float = 1.0) -> dict:
|
|
378
|
+
plot_kwargs = self.matplot_kwargs(scale)
|
|
379
|
+
plot_kwargs["edgecolors"] = plot_kwargs.pop("markeredgecolor")
|
|
380
|
+
|
|
381
|
+
# matplotlib's plot() function takes the marker size in points diameter
|
|
382
|
+
# and the scatter() function takes it in points squared
|
|
383
|
+
plot_kwargs["s"] = ((plot_kwargs.pop("markersize") / scale) ** 2) * (scale**2)
|
|
384
|
+
|
|
385
|
+
plot_kwargs["c"] = plot_kwargs.pop("color")
|
|
386
|
+
plot_kwargs["linewidths"] = self.edge_width * scale
|
|
387
|
+
plot_kwargs["linestyle"] = self.line_style
|
|
388
|
+
plot_kwargs["capstyle"] = self.dash_capstyle
|
|
389
|
+
|
|
390
|
+
plot_kwargs.pop("fillstyle")
|
|
391
|
+
|
|
392
|
+
return plot_kwargs
|
|
393
|
+
|
|
394
|
+
def to_polygon_style(self):
|
|
395
|
+
return PolygonStyle(
|
|
396
|
+
fill_color=self.color.as_hex() if self.color else None,
|
|
397
|
+
edge_color=self.edge_color.as_hex() if self.edge_color else None,
|
|
398
|
+
edge_width=self.edge_width,
|
|
399
|
+
alpha=self.alpha,
|
|
400
|
+
zorder=self.zorder,
|
|
401
|
+
line_style=self.line_style,
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
class LineStyle(BaseStyle):
|
|
406
|
+
"""
|
|
407
|
+
Styling properties for lines.
|
|
408
|
+
"""
|
|
409
|
+
|
|
410
|
+
width: float = 4
|
|
411
|
+
"""Width of line in points"""
|
|
412
|
+
|
|
413
|
+
color: ColorStr = ColorStr("#000")
|
|
414
|
+
"""Color of the line. Can be a hex, rgb, hsl, or word string."""
|
|
415
|
+
|
|
416
|
+
style: Union[LineStyleEnum, tuple] = LineStyleEnum.SOLID
|
|
417
|
+
"""Style of the line (e.g. solid, dashed, etc). Can be a predefined value in `LineStyleEnum` or a [Matplotlib linestyle tuple](https://matplotlib.org/stable/gallery/lines_bars_and_markers/linestyles.html)."""
|
|
418
|
+
|
|
419
|
+
dash_capstyle: CapStyleEnum = CapStyleEnum.PROJECTING
|
|
420
|
+
"""Style of dash endpoints"""
|
|
421
|
+
|
|
422
|
+
alpha: float = 1.0
|
|
423
|
+
"""Alpha value (controls transparency)"""
|
|
424
|
+
|
|
425
|
+
zorder: int = ZOrderEnum.LAYER_2
|
|
426
|
+
"""Zorder of the line"""
|
|
427
|
+
|
|
428
|
+
edge_width: int = 0
|
|
429
|
+
"""Width of the line's edge in points. _If the width or color is falsey then the line will NOT be drawn with an edge._"""
|
|
430
|
+
|
|
431
|
+
edge_color: Optional[ColorStr] = None
|
|
432
|
+
"""Edge color of the line. _If the width or color is falsey then the line will NOT be drawn with an edge._"""
|
|
433
|
+
|
|
434
|
+
def matplot_kwargs(self, scale: float = 1.0) -> dict:
|
|
435
|
+
line_width = self.width * scale
|
|
436
|
+
|
|
437
|
+
result = dict(
|
|
438
|
+
color=self.color.as_hex(),
|
|
439
|
+
linestyle=self.style,
|
|
440
|
+
linewidth=line_width,
|
|
441
|
+
# dash_capstyle=self.dash_capstyle,
|
|
442
|
+
alpha=self.alpha,
|
|
443
|
+
zorder=self.zorder,
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
if self.edge_width and self.edge_color:
|
|
447
|
+
result["path_effects"] = [
|
|
448
|
+
patheffects.withStroke(
|
|
449
|
+
linewidth=line_width + 2 * self.edge_width * scale,
|
|
450
|
+
foreground=self.edge_color.as_hex(),
|
|
451
|
+
)
|
|
452
|
+
]
|
|
453
|
+
|
|
454
|
+
return result
|
|
455
|
+
|
|
456
|
+
def matplot_line_collection_kwargs(self, scale: float = 1.0) -> dict:
|
|
457
|
+
plot_kwargs = self.matplot_kwargs(scale)
|
|
458
|
+
plot_kwargs["linewidths"] = plot_kwargs.pop("linewidth")
|
|
459
|
+
plot_kwargs["colors"] = plot_kwargs.pop("color")
|
|
460
|
+
return plot_kwargs
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
class PolygonStyle(BaseStyle):
|
|
464
|
+
"""
|
|
465
|
+
Styling properties for polygons.
|
|
466
|
+
"""
|
|
467
|
+
|
|
468
|
+
edge_width: float = 1
|
|
469
|
+
"""Width of the polygon's edge in points"""
|
|
470
|
+
|
|
471
|
+
color: Optional[ColorStr] = None
|
|
472
|
+
"""If specified, this will be the fill color AND edge color of the polygon"""
|
|
473
|
+
|
|
474
|
+
edge_color: Optional[ColorStr] = None
|
|
475
|
+
"""Edge color of the polygon"""
|
|
476
|
+
|
|
477
|
+
fill_color: Optional[ColorStr] = None
|
|
478
|
+
"""Fill color of the polygon"""
|
|
479
|
+
|
|
480
|
+
line_style: Union[LineStyleEnum, tuple] = LineStyleEnum.SOLID
|
|
481
|
+
"""Edge line style. Can be a predefined value in `LineStyleEnum` or a [Matplotlib linestyle tuple](https://matplotlib.org/stable/gallery/lines_bars_and_markers/linestyles.html)."""
|
|
482
|
+
|
|
483
|
+
alpha: float = 1.0
|
|
484
|
+
"""Alpha value (controls transparency)"""
|
|
485
|
+
|
|
486
|
+
zorder: int = -1
|
|
487
|
+
"""Zorder of the polygon"""
|
|
488
|
+
|
|
489
|
+
def matplot_kwargs(self, scale: float = 1.0) -> dict:
|
|
490
|
+
styles = dict(
|
|
491
|
+
edgecolor=self.edge_color.as_hex() if self.edge_color else "none",
|
|
492
|
+
facecolor=self.fill_color.as_hex() if self.fill_color else "none",
|
|
493
|
+
fill=True if self.fill_color or self.color else False,
|
|
494
|
+
linewidth=self.edge_width * scale,
|
|
495
|
+
linestyle=self.line_style,
|
|
496
|
+
alpha=self.alpha,
|
|
497
|
+
zorder=self.zorder,
|
|
498
|
+
capstyle="round",
|
|
499
|
+
)
|
|
500
|
+
if self.color:
|
|
501
|
+
styles["color"] = self.color.as_hex()
|
|
502
|
+
|
|
503
|
+
return styles
|
|
504
|
+
|
|
505
|
+
def to_marker_style(self, symbol: MarkerSymbolEnum):
|
|
506
|
+
color = self.color.as_hex() if self.color else None
|
|
507
|
+
fill_color = self.fill_color.as_hex() if self.fill_color else None
|
|
508
|
+
fill_style = FillStyleEnum.FULL if color or fill_color else FillStyleEnum.NONE
|
|
509
|
+
return MarkerStyle(
|
|
510
|
+
symbol=symbol,
|
|
511
|
+
color=color or fill_color,
|
|
512
|
+
fill=fill_style,
|
|
513
|
+
edge_color=self.edge_color.as_hex() if self.edge_color else None,
|
|
514
|
+
edge_width=self.edge_width,
|
|
515
|
+
alpha=self.alpha,
|
|
516
|
+
zorder=self.zorder,
|
|
517
|
+
line_style=self.line_style,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
class ArrowStyle(PolygonStyle):
|
|
522
|
+
body_width: float = 24
|
|
523
|
+
"""Width of the arrow's body, in pixels"""
|
|
524
|
+
|
|
525
|
+
head_width: float = 60
|
|
526
|
+
"""Width of the arrow's head, in pixels"""
|
|
527
|
+
|
|
528
|
+
head_height: float = 100
|
|
529
|
+
"""Height of the arrow's head, in pixels"""
|
|
530
|
+
|
|
531
|
+
cap_style: CapStyleEnum = CapStyleEnum.BUTT
|
|
532
|
+
"""Cap style of the arrow"""
|
|
533
|
+
|
|
534
|
+
join_style: JoinStyleEnum = JoinStyleEnum.MITRE
|
|
535
|
+
"""Join style of the arrow"""
|
|
536
|
+
|
|
537
|
+
def shapely_kwargs(self):
|
|
538
|
+
cap_styles = {
|
|
539
|
+
CapStyleEnum.BUTT: "flat",
|
|
540
|
+
CapStyleEnum.ROUND: "round",
|
|
541
|
+
CapStyleEnum.PROJECTING: "square",
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
"cap_style": cap_styles[self.cap_style],
|
|
545
|
+
"join_style": self.join_style,
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class LabelStyle(BaseStyle):
|
|
550
|
+
"""
|
|
551
|
+
Styling properties for a label.
|
|
552
|
+
"""
|
|
553
|
+
|
|
554
|
+
font_size: float = 15
|
|
555
|
+
"""Font size of the label, in points"""
|
|
556
|
+
|
|
557
|
+
font_weight: FontWeightEnum = FontWeightEnum.NORMAL
|
|
558
|
+
"""Font weight (e.g. normal, bold, ultra bold, etc)"""
|
|
559
|
+
|
|
560
|
+
font_color: ColorStr = ColorStr("#000")
|
|
561
|
+
"""Font's color"""
|
|
562
|
+
|
|
563
|
+
font_alpha: float = 1
|
|
564
|
+
"""Font's alpha (transparency)"""
|
|
565
|
+
|
|
566
|
+
font_style: FontStyleEnum = FontStyleEnum.NORMAL
|
|
567
|
+
"""Style of the label (e.g. normal, italic, etc)"""
|
|
568
|
+
|
|
569
|
+
font_name: Optional[str] = "Inter"
|
|
570
|
+
"""Name of the font to use"""
|
|
571
|
+
|
|
572
|
+
font_family: Optional[str] = None
|
|
573
|
+
"""Font family (e.g. 'monospace', 'sans-serif', 'serif', etc)"""
|
|
574
|
+
|
|
575
|
+
line_spacing: Optional[float] = None
|
|
576
|
+
"""Spacing between lines of text"""
|
|
577
|
+
|
|
578
|
+
anchor_point: AnchorPointEnum = AnchorPointEnum.BOTTOM_RIGHT
|
|
579
|
+
"""Anchor point of label"""
|
|
580
|
+
|
|
581
|
+
border_width: float = 0
|
|
582
|
+
"""Width of border (also known as 'halos') around the text, in points"""
|
|
583
|
+
|
|
584
|
+
border_color: Optional[ColorStr] = None
|
|
585
|
+
"""Color of border (also known as 'halos') around the text"""
|
|
586
|
+
|
|
587
|
+
offset_x: Union[float, int, str] = 0
|
|
588
|
+
"""
|
|
589
|
+
Horizontal offset of the label, in points. Negative values supported.
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
**Auto Mode** (_experimental_): If the label is plotted as part of a marker (e.g. stars, via `marker()`, etc), then you can also
|
|
593
|
+
specify the offset as `"auto"` which will calculate the offset automatically based on the marker's size and place
|
|
594
|
+
the label just outside the marker (avoiding overlapping). To enable "auto" mode you have to specify BOTH offsets (x and y) as "auto."
|
|
595
|
+
"""
|
|
596
|
+
|
|
597
|
+
offset_y: Union[float, int, str] = 0
|
|
598
|
+
"""
|
|
599
|
+
Vertical offset of the label, in points. Negative values supported.
|
|
600
|
+
|
|
601
|
+
**Auto Mode** (_experimental_): If the label is plotted as part of a marker (e.g. stars, via `marker()`, etc), then you can also
|
|
602
|
+
specify the offset as `"auto"` which will calculate the offset automatically based on the marker's size and place
|
|
603
|
+
the label just outside the marker (avoiding overlapping). To enable "auto" mode you have to specify BOTH offsets (x and y) as "auto."
|
|
604
|
+
"""
|
|
605
|
+
|
|
606
|
+
zorder: int = ZOrderEnum.LAYER_4
|
|
607
|
+
"""Zorder of the label"""
|
|
608
|
+
|
|
609
|
+
def matplot_kwargs(self, scale: float = 1.0) -> dict:
|
|
610
|
+
style = dict(
|
|
611
|
+
color=self.font_color.as_hex(),
|
|
612
|
+
fontsize=self.font_size * scale,
|
|
613
|
+
fontstyle=self.font_style,
|
|
614
|
+
fontname=self.font_name,
|
|
615
|
+
weight=FontWeightEnum(self.font_weight).as_matplot(),
|
|
616
|
+
alpha=self.font_alpha,
|
|
617
|
+
zorder=self.zorder,
|
|
618
|
+
)
|
|
619
|
+
|
|
620
|
+
if self.font_family:
|
|
621
|
+
style["family"] = self.font_family
|
|
622
|
+
if self.line_spacing:
|
|
623
|
+
style["linespacing"] = self.line_spacing
|
|
624
|
+
|
|
625
|
+
if self.border_width != 0 and self.border_color is not None:
|
|
626
|
+
style["path_effects"] = [
|
|
627
|
+
patheffects.withStroke(
|
|
628
|
+
linewidth=self.border_width * scale,
|
|
629
|
+
foreground=self.border_color.as_hex(),
|
|
630
|
+
)
|
|
631
|
+
]
|
|
632
|
+
|
|
633
|
+
style.update(AnchorPointEnum(self.anchor_point).as_matplot())
|
|
634
|
+
|
|
635
|
+
return style
|
|
636
|
+
|
|
637
|
+
def offset_from_marker(self, marker_symbol, marker_size, scale: float = 1.0):
|
|
638
|
+
"""Handles auto offsets from marker"""
|
|
639
|
+
|
|
640
|
+
if self.offset_x != "auto" or self.offset_y != "auto":
|
|
641
|
+
return self
|
|
642
|
+
|
|
643
|
+
new_style = self.model_copy()
|
|
644
|
+
|
|
645
|
+
x_direction = -1 if new_style.anchor_point.endswith("left") else 1
|
|
646
|
+
y_direction = -1 if new_style.anchor_point.startswith("bottom") else 1
|
|
647
|
+
|
|
648
|
+
offset = (marker_size**0.5 / 2) / scale
|
|
649
|
+
|
|
650
|
+
# matplotlib seems to use marker size differently depending on symbol (for scatter)
|
|
651
|
+
# it is NOT strictly the area of the bounding box of the marker
|
|
652
|
+
if marker_symbol in [MarkerSymbolEnum.POINT]:
|
|
653
|
+
offset /= PI
|
|
654
|
+
|
|
655
|
+
elif marker_symbol != MarkerSymbolEnum.SQUARE:
|
|
656
|
+
offset /= SQR_2
|
|
657
|
+
offset *= scale
|
|
658
|
+
|
|
659
|
+
offset += 0.65
|
|
660
|
+
new_style.offset_x = offset * float(x_direction)
|
|
661
|
+
new_style.offset_y = offset * float(y_direction)
|
|
662
|
+
|
|
663
|
+
return new_style
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
class ObjectStyle(BaseStyle):
|
|
667
|
+
"""Defines the style for a sky object (e.g. star, DSO)"""
|
|
668
|
+
|
|
669
|
+
marker: MarkerStyle = MarkerStyle()
|
|
670
|
+
"""Style for the object's marker (see [MarkerStyle][starplot.styles.MarkerStyle])"""
|
|
671
|
+
|
|
672
|
+
label: LabelStyle = LabelStyle()
|
|
673
|
+
"""Style for the object's label (see [LabelStyle][starplot.styles.LabelStyle])"""
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
class PathStyle(BaseStyle):
|
|
677
|
+
"""Defines the style for a path (e.g. constellation lines)"""
|
|
678
|
+
|
|
679
|
+
line: LineStyle = LineStyle()
|
|
680
|
+
"""Style for the line (see [LineStyle][starplot.styles.LineStyle])"""
|
|
681
|
+
|
|
682
|
+
label: LabelStyle = LabelStyle()
|
|
683
|
+
"""Style for the path's label (see [LabelStyle][starplot.styles.LabelStyle])"""
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
class LegendStyle(BaseStyle):
|
|
687
|
+
"""Defines the style for the map legend. *Only applies to map plots.*"""
|
|
688
|
+
|
|
689
|
+
alignment: AlignmentEnum = AlignmentEnum.LEFT
|
|
690
|
+
"""Alignment for the legend's title and entries"""
|
|
691
|
+
|
|
692
|
+
location: LegendLocationEnum = LegendLocationEnum.INSIDE_BOTTOM_RIGHT
|
|
693
|
+
"""Location of the legend, relative to the map area (inside or outside)"""
|
|
694
|
+
|
|
695
|
+
background_color: ColorStr = ColorStr("#fff")
|
|
696
|
+
"""Background color of the legend box"""
|
|
697
|
+
|
|
698
|
+
background_alpha: float = 1.0
|
|
699
|
+
"""Background's alpha (transparency)"""
|
|
700
|
+
|
|
701
|
+
padding: float = 0
|
|
702
|
+
"""Padding on the outside of the legend. Negative numbers are supported."""
|
|
703
|
+
|
|
704
|
+
padding_x: float = 0
|
|
705
|
+
"""Padding (in pixels) between the _outside_ of the legend and the map in the X axis. Negative numbers are supported."""
|
|
706
|
+
|
|
707
|
+
padding_y: float = 0
|
|
708
|
+
"""Padding (in pixels) between the _outside_ of the legend and the map in the Y axis. Negative numbers are supported."""
|
|
709
|
+
|
|
710
|
+
expand: bool = False
|
|
711
|
+
"""If True, the legend will be expanded to fit the full width of the map"""
|
|
712
|
+
|
|
713
|
+
num_columns: int = 8
|
|
714
|
+
"""Number of columns in the legend"""
|
|
715
|
+
|
|
716
|
+
label_padding: float = 1.6
|
|
717
|
+
"""Padding between legend labels"""
|
|
718
|
+
|
|
719
|
+
symbol_size: int = 34
|
|
720
|
+
"""Size of symbols in the legend, in points"""
|
|
721
|
+
|
|
722
|
+
symbol_padding: float = 0.2
|
|
723
|
+
"""Padding between each symbol and its label"""
|
|
724
|
+
|
|
725
|
+
border_color: ColorStr = ColorStr("#c5c5c5")
|
|
726
|
+
"""Border color of the legend box"""
|
|
727
|
+
|
|
728
|
+
border_padding: float = 1.28
|
|
729
|
+
"""Padding between legend entries and the legend border"""
|
|
730
|
+
|
|
731
|
+
font_name: str = "Inter"
|
|
732
|
+
"""Font name for legend labels"""
|
|
733
|
+
|
|
734
|
+
font_size: int = 23
|
|
735
|
+
"""Font size of the legend labels, in points"""
|
|
736
|
+
|
|
737
|
+
font_weight: FontWeightEnum = FontWeightEnum.NORMAL
|
|
738
|
+
"""Font weight of the legend labels"""
|
|
739
|
+
|
|
740
|
+
font_color: ColorStr = ColorStr("#000")
|
|
741
|
+
"""Font color for legend labels"""
|
|
742
|
+
|
|
743
|
+
title_font_size: int = 36
|
|
744
|
+
"""Font size of the legend title"""
|
|
745
|
+
|
|
746
|
+
title_font_weight: FontWeightEnum = FontWeightEnum.BOLD
|
|
747
|
+
"""Font weight of the legend title"""
|
|
748
|
+
|
|
749
|
+
title_font_name: str = "Inter"
|
|
750
|
+
"""Name of the font to use for the title. Comma-separated list."""
|
|
751
|
+
|
|
752
|
+
zorder: int = ZOrderEnum.LAYER_5
|
|
753
|
+
"""Zorder of the legend"""
|
|
754
|
+
|
|
755
|
+
def matplot_kwargs(self, scale: float = 1.0) -> dict:
|
|
756
|
+
return dict(
|
|
757
|
+
loc=self.location,
|
|
758
|
+
ncols=self.num_columns,
|
|
759
|
+
framealpha=self.background_alpha,
|
|
760
|
+
prop={
|
|
761
|
+
"family": self.font_name,
|
|
762
|
+
"weight": FontWeightEnum(self.font_weight).as_matplot(),
|
|
763
|
+
"size": self.font_size * scale,
|
|
764
|
+
},
|
|
765
|
+
labelcolor=self.font_color.as_hex(),
|
|
766
|
+
borderpad=self.border_padding,
|
|
767
|
+
labelspacing=self.label_padding,
|
|
768
|
+
handletextpad=self.symbol_padding,
|
|
769
|
+
mode="expand" if self.expand else None,
|
|
770
|
+
facecolor=self.background_color.as_hex(),
|
|
771
|
+
title_fontproperties=dict(
|
|
772
|
+
weight=FontWeightEnum(self.title_font_weight).as_matplot(),
|
|
773
|
+
size=self.title_font_size,
|
|
774
|
+
family=self.title_font_name.split(","),
|
|
775
|
+
),
|
|
776
|
+
alignment=self.alignment,
|
|
777
|
+
edgecolor=self.border_color.as_hex(),
|
|
778
|
+
borderaxespad=self.padding,
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
|
|
782
|
+
class PlotStyle(BaseStyle):
|
|
783
|
+
"""
|
|
784
|
+
Defines the styling for a plot
|
|
785
|
+
"""
|
|
786
|
+
|
|
787
|
+
background_color: list[tuple[float, str]] | ColorStr = ColorStr("#fff")
|
|
788
|
+
"""
|
|
789
|
+
Background color of the map region.
|
|
790
|
+
|
|
791
|
+
This can either be a single color (e.g. `#7abfff`) or a list that defines a gradient.
|
|
792
|
+
|
|
793
|
+
For gradients, the list items should be tuples with two elements: a float that defines
|
|
794
|
+
the stop and a string that defines the color for that stop. For example:
|
|
795
|
+
|
|
796
|
+
```
|
|
797
|
+
"background_color": [
|
|
798
|
+
(0.0, "#7abfff"),
|
|
799
|
+
(0.2, "#7abfff"),
|
|
800
|
+
(0.9, "#568feb"),
|
|
801
|
+
(1.0, "#3f7ee3"), # the last stop should always be at 1.0
|
|
802
|
+
]
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
There are a few predefined gradients available as [style extensions](/reference-styling/#style-extensions).
|
|
806
|
+
|
|
807
|
+
**Gradient backgrounds are not yet supported for optic plots that use a camera.**
|
|
808
|
+
"""
|
|
809
|
+
|
|
810
|
+
figure_background_color: ColorStr = ColorStr("#fff")
|
|
811
|
+
|
|
812
|
+
text_border_width: int = 2
|
|
813
|
+
"""Text border (aka halos) width. This will apply to _all_ text labels on the plot. If you'd like to control these borders by object type, then set this global width to `0` and refer to the label style's `border_width` and `border_color` properties."""
|
|
814
|
+
|
|
815
|
+
text_border_color: ColorStr = ColorStr("#fff")
|
|
816
|
+
|
|
817
|
+
# Borders
|
|
818
|
+
border_font_size: int = 18
|
|
819
|
+
border_font_weight: FontWeightEnum = FontWeightEnum.BOLD
|
|
820
|
+
border_font_color: ColorStr = ColorStr("#000")
|
|
821
|
+
border_line_color: ColorStr = ColorStr("#000")
|
|
822
|
+
border_bg_color: ColorStr = ColorStr("#fff")
|
|
823
|
+
|
|
824
|
+
# Title
|
|
825
|
+
title: LabelStyle = LabelStyle(
|
|
826
|
+
font_size=70,
|
|
827
|
+
font_weight=FontWeightEnum.BOLD,
|
|
828
|
+
zorder=ZOrderEnum.LAYER_5,
|
|
829
|
+
line_spacing=150,
|
|
830
|
+
anchor_point=AnchorPointEnum.BOTTOM_CENTER,
|
|
831
|
+
)
|
|
832
|
+
"""Styling for info text (only applies to zenith and optic plots)"""
|
|
833
|
+
|
|
834
|
+
# Info text
|
|
835
|
+
info_text: LabelStyle = LabelStyle(
|
|
836
|
+
font_size=30,
|
|
837
|
+
zorder=ZOrderEnum.LAYER_5,
|
|
838
|
+
font_family="Inter",
|
|
839
|
+
line_spacing=1.2,
|
|
840
|
+
anchor_point=AnchorPointEnum.BOTTOM_CENTER,
|
|
841
|
+
)
|
|
842
|
+
"""Styling for info text (only applies to zenith and optic plots)"""
|
|
843
|
+
|
|
844
|
+
# Stars
|
|
845
|
+
star: ObjectStyle = ObjectStyle(
|
|
846
|
+
marker=MarkerStyle(
|
|
847
|
+
fill=FillStyleEnum.FULL,
|
|
848
|
+
zorder=ZOrderEnum.LAYER_3 + 1,
|
|
849
|
+
size=40,
|
|
850
|
+
edge_color=None,
|
|
851
|
+
),
|
|
852
|
+
label=LabelStyle(
|
|
853
|
+
font_size=24,
|
|
854
|
+
font_weight=FontWeightEnum.BOLD,
|
|
855
|
+
zorder=ZOrderEnum.LAYER_3 + 2,
|
|
856
|
+
offset_x="auto",
|
|
857
|
+
offset_y="auto",
|
|
858
|
+
),
|
|
859
|
+
)
|
|
860
|
+
"""Styling for stars *(see [`ObjectStyle`][starplot.styles.ObjectStyle])*"""
|
|
861
|
+
|
|
862
|
+
bayer_labels: LabelStyle = LabelStyle(
|
|
863
|
+
font_size=21,
|
|
864
|
+
font_weight=FontWeightEnum.EXTRA_LIGHT,
|
|
865
|
+
font_name="GFS Didot",
|
|
866
|
+
zorder=ZOrderEnum.LAYER_4,
|
|
867
|
+
anchor_point=AnchorPointEnum.TOP_LEFT,
|
|
868
|
+
offset_x="auto",
|
|
869
|
+
offset_y="auto",
|
|
870
|
+
)
|
|
871
|
+
"""Styling for Bayer labels of stars"""
|
|
872
|
+
|
|
873
|
+
flamsteed_labels: LabelStyle = LabelStyle(
|
|
874
|
+
font_size=13,
|
|
875
|
+
font_weight=FontWeightEnum.NORMAL,
|
|
876
|
+
zorder=ZOrderEnum.LAYER_4,
|
|
877
|
+
anchor_point=AnchorPointEnum.BOTTOM_LEFT,
|
|
878
|
+
offset_x="auto",
|
|
879
|
+
offset_y="auto",
|
|
880
|
+
)
|
|
881
|
+
"""Styling for Flamsteed number labels of stars"""
|
|
882
|
+
|
|
883
|
+
planets: ObjectStyle = ObjectStyle(
|
|
884
|
+
marker=MarkerStyle(
|
|
885
|
+
symbol=MarkerSymbolEnum.CIRCLE,
|
|
886
|
+
size=28,
|
|
887
|
+
fill=FillStyleEnum.LEFT,
|
|
888
|
+
zorder=ZOrderEnum.LAYER_3,
|
|
889
|
+
alpha=1,
|
|
890
|
+
),
|
|
891
|
+
label=LabelStyle(
|
|
892
|
+
font_size=28,
|
|
893
|
+
font_weight=FontWeightEnum.BOLD,
|
|
894
|
+
offset_x="auto",
|
|
895
|
+
offset_y="auto",
|
|
896
|
+
),
|
|
897
|
+
)
|
|
898
|
+
"""Styling for planets"""
|
|
899
|
+
|
|
900
|
+
moon: ObjectStyle = ObjectStyle(
|
|
901
|
+
marker=MarkerStyle(
|
|
902
|
+
symbol=MarkerSymbolEnum.CIRCLE,
|
|
903
|
+
size=50,
|
|
904
|
+
fill=FillStyleEnum.FULL,
|
|
905
|
+
color="#c8c8c8",
|
|
906
|
+
alpha=1,
|
|
907
|
+
zorder=ZOrderEnum.LAYER_4,
|
|
908
|
+
),
|
|
909
|
+
label=LabelStyle(
|
|
910
|
+
font_size=28,
|
|
911
|
+
font_weight=FontWeightEnum.BOLD,
|
|
912
|
+
offset_x="auto",
|
|
913
|
+
offset_y="auto",
|
|
914
|
+
),
|
|
915
|
+
)
|
|
916
|
+
"""Styling for the moon"""
|
|
917
|
+
|
|
918
|
+
sun: ObjectStyle = ObjectStyle(
|
|
919
|
+
marker=MarkerStyle(
|
|
920
|
+
symbol=MarkerSymbolEnum.SUN,
|
|
921
|
+
size=80,
|
|
922
|
+
fill=FillStyleEnum.FULL,
|
|
923
|
+
color="#000",
|
|
924
|
+
zorder=ZOrderEnum.LAYER_4 - 100,
|
|
925
|
+
),
|
|
926
|
+
label=LabelStyle(
|
|
927
|
+
font_size=28,
|
|
928
|
+
font_weight=FontWeightEnum.BOLD,
|
|
929
|
+
),
|
|
930
|
+
)
|
|
931
|
+
"""Styling for the Sun"""
|
|
932
|
+
|
|
933
|
+
# Deep Sky Objects (DSOs)
|
|
934
|
+
dso_open_cluster: ObjectStyle = ObjectStyle(
|
|
935
|
+
marker=MarkerStyle(
|
|
936
|
+
symbol=MarkerSymbolEnum.CIRCLE,
|
|
937
|
+
fill=FillStyleEnum.FULL,
|
|
938
|
+
line_style=(0, (1, 2)),
|
|
939
|
+
edge_width=1.3,
|
|
940
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
941
|
+
),
|
|
942
|
+
label=LabelStyle(offset_x="auto", offset_y="auto"),
|
|
943
|
+
)
|
|
944
|
+
"""Styling for open star clusters"""
|
|
945
|
+
|
|
946
|
+
dso_association_stars: ObjectStyle = ObjectStyle(
|
|
947
|
+
marker=MarkerStyle(
|
|
948
|
+
symbol=MarkerSymbolEnum.CIRCLE,
|
|
949
|
+
fill=FillStyleEnum.FULL,
|
|
950
|
+
line_style=(0, (1, 2)),
|
|
951
|
+
edge_width=1.3,
|
|
952
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
953
|
+
),
|
|
954
|
+
label=LabelStyle(offset_x="auto", offset_y="auto"),
|
|
955
|
+
)
|
|
956
|
+
"""Styling for associations of stars"""
|
|
957
|
+
|
|
958
|
+
dso_globular_cluster: ObjectStyle = ObjectStyle(
|
|
959
|
+
marker=MarkerStyle(
|
|
960
|
+
symbol=MarkerSymbolEnum.CIRCLE_CROSS,
|
|
961
|
+
fill=FillStyleEnum.FULL,
|
|
962
|
+
color="#555",
|
|
963
|
+
alpha=0.8,
|
|
964
|
+
edge_width=1.2,
|
|
965
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
966
|
+
),
|
|
967
|
+
label=LabelStyle(offset_x="auto", offset_y="auto"),
|
|
968
|
+
)
|
|
969
|
+
"""Styling for globular star clusters"""
|
|
970
|
+
|
|
971
|
+
dso_galaxy: ObjectStyle = ObjectStyle(
|
|
972
|
+
marker=MarkerStyle(
|
|
973
|
+
symbol=MarkerSymbolEnum.ELLIPSE,
|
|
974
|
+
fill=FillStyleEnum.FULL,
|
|
975
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
976
|
+
),
|
|
977
|
+
label=LabelStyle(offset_x="auto", offset_y="auto"),
|
|
978
|
+
)
|
|
979
|
+
"""Styling for galaxies"""
|
|
980
|
+
|
|
981
|
+
dso_nebula: ObjectStyle = ObjectStyle(
|
|
982
|
+
marker=MarkerStyle(
|
|
983
|
+
symbol=MarkerSymbolEnum.SQUARE,
|
|
984
|
+
fill=FillStyleEnum.FULL,
|
|
985
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
986
|
+
),
|
|
987
|
+
label=LabelStyle(offset_x="auto", offset_y="auto"),
|
|
988
|
+
)
|
|
989
|
+
"""Styling for nebulas"""
|
|
990
|
+
|
|
991
|
+
dso_planetary_nebula: ObjectStyle = ObjectStyle(
|
|
992
|
+
marker=MarkerStyle(
|
|
993
|
+
symbol=MarkerSymbolEnum.CIRCLE_CROSSHAIR,
|
|
994
|
+
fill=FillStyleEnum.FULL,
|
|
995
|
+
edge_width=1.6,
|
|
996
|
+
size=26,
|
|
997
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
998
|
+
),
|
|
999
|
+
label=LabelStyle(offset_x="auto", offset_y="auto"),
|
|
1000
|
+
)
|
|
1001
|
+
"""Styling for planetary nebulas"""
|
|
1002
|
+
|
|
1003
|
+
dso_double_star: ObjectStyle = ObjectStyle(
|
|
1004
|
+
marker=MarkerStyle(
|
|
1005
|
+
symbol=MarkerSymbolEnum.CIRCLE_LINE,
|
|
1006
|
+
fill=FillStyleEnum.TOP,
|
|
1007
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
1008
|
+
),
|
|
1009
|
+
label=LabelStyle(offset_x=1, offset_y=-1),
|
|
1010
|
+
)
|
|
1011
|
+
"""Styling for double stars"""
|
|
1012
|
+
|
|
1013
|
+
dso_dark_nebula: ObjectStyle = ObjectStyle(
|
|
1014
|
+
marker=MarkerStyle(
|
|
1015
|
+
symbol=MarkerSymbolEnum.SQUARE,
|
|
1016
|
+
fill=FillStyleEnum.TOP,
|
|
1017
|
+
color="#000",
|
|
1018
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
1019
|
+
),
|
|
1020
|
+
label=LabelStyle(),
|
|
1021
|
+
)
|
|
1022
|
+
"""Styling for dark nebulas"""
|
|
1023
|
+
|
|
1024
|
+
dso_supernova_remnant: ObjectStyle = ObjectStyle(
|
|
1025
|
+
marker=MarkerStyle(
|
|
1026
|
+
symbol=MarkerSymbolEnum.SQUARE,
|
|
1027
|
+
fill=FillStyleEnum.TOP,
|
|
1028
|
+
color="#000",
|
|
1029
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
1030
|
+
),
|
|
1031
|
+
label=LabelStyle(),
|
|
1032
|
+
)
|
|
1033
|
+
"""Styling for supernova remnants"""
|
|
1034
|
+
|
|
1035
|
+
dso_nova_star: ObjectStyle = ObjectStyle(
|
|
1036
|
+
marker=MarkerStyle(
|
|
1037
|
+
symbol=MarkerSymbolEnum.SQUARE,
|
|
1038
|
+
fill=FillStyleEnum.TOP,
|
|
1039
|
+
color="#000",
|
|
1040
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
1041
|
+
),
|
|
1042
|
+
label=LabelStyle(),
|
|
1043
|
+
)
|
|
1044
|
+
"""Styling for nova stars"""
|
|
1045
|
+
|
|
1046
|
+
dso_nonexistant: ObjectStyle = ObjectStyle(
|
|
1047
|
+
marker=MarkerStyle(
|
|
1048
|
+
symbol=MarkerSymbolEnum.SQUARE,
|
|
1049
|
+
fill=FillStyleEnum.TOP,
|
|
1050
|
+
color="#000",
|
|
1051
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
1052
|
+
),
|
|
1053
|
+
label=LabelStyle(),
|
|
1054
|
+
)
|
|
1055
|
+
"""Styling for 'nonexistent' (as designated by OpenNGC) deep sky objects"""
|
|
1056
|
+
|
|
1057
|
+
dso_unknown: ObjectStyle = ObjectStyle(
|
|
1058
|
+
marker=MarkerStyle(
|
|
1059
|
+
symbol=MarkerSymbolEnum.SQUARE,
|
|
1060
|
+
fill=FillStyleEnum.TOP,
|
|
1061
|
+
color="#000",
|
|
1062
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
1063
|
+
),
|
|
1064
|
+
label=LabelStyle(),
|
|
1065
|
+
)
|
|
1066
|
+
"""Styling for 'unknown' (as designated by OpenNGC) types of deep sky objects"""
|
|
1067
|
+
|
|
1068
|
+
dso_duplicate: ObjectStyle = ObjectStyle(
|
|
1069
|
+
marker=MarkerStyle(
|
|
1070
|
+
symbol=MarkerSymbolEnum.SQUARE,
|
|
1071
|
+
fill=FillStyleEnum.TOP,
|
|
1072
|
+
color="#000",
|
|
1073
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
1074
|
+
),
|
|
1075
|
+
label=LabelStyle(),
|
|
1076
|
+
)
|
|
1077
|
+
"""Styling for 'duplicate record' (as designated by OpenNGC) types of deep sky objects"""
|
|
1078
|
+
|
|
1079
|
+
constellation_lines: LineStyle = LineStyle(
|
|
1080
|
+
color="#c8c8c8", zorder=ZOrderEnum.LAYER_3
|
|
1081
|
+
)
|
|
1082
|
+
"""Styling for constellation lines"""
|
|
1083
|
+
|
|
1084
|
+
constellation_borders: LineStyle = LineStyle(
|
|
1085
|
+
color="#000",
|
|
1086
|
+
width=1.5,
|
|
1087
|
+
style=LineStyleEnum.DASHED,
|
|
1088
|
+
alpha=0.4,
|
|
1089
|
+
zorder=ZOrderEnum.LAYER_3,
|
|
1090
|
+
)
|
|
1091
|
+
"""Styling for constellation borders"""
|
|
1092
|
+
|
|
1093
|
+
constellation_labels: LabelStyle = LabelStyle(
|
|
1094
|
+
font_size=21,
|
|
1095
|
+
font_weight=FontWeightEnum.NORMAL,
|
|
1096
|
+
zorder=ZOrderEnum.LAYER_3,
|
|
1097
|
+
anchor_point=AnchorPointEnum.CENTER,
|
|
1098
|
+
)
|
|
1099
|
+
"""Styling for constellation labels"""
|
|
1100
|
+
|
|
1101
|
+
# Milky Way
|
|
1102
|
+
milky_way: PolygonStyle = PolygonStyle(
|
|
1103
|
+
fill_color="#d9d9d9",
|
|
1104
|
+
alpha=0.36,
|
|
1105
|
+
edge_width=0,
|
|
1106
|
+
zorder=ZOrderEnum.LAYER_1,
|
|
1107
|
+
)
|
|
1108
|
+
"""Styling for the Milky Way (only applies to map plots)"""
|
|
1109
|
+
|
|
1110
|
+
# Legend
|
|
1111
|
+
legend: LegendStyle = LegendStyle()
|
|
1112
|
+
"""Styling for legend"""
|
|
1113
|
+
|
|
1114
|
+
# Gridlines
|
|
1115
|
+
gridlines: PathStyle = PathStyle(
|
|
1116
|
+
line=LineStyle(
|
|
1117
|
+
color="#888",
|
|
1118
|
+
width=1,
|
|
1119
|
+
style=LineStyleEnum.SOLID,
|
|
1120
|
+
alpha=0.8,
|
|
1121
|
+
zorder=ZOrderEnum.LAYER_2,
|
|
1122
|
+
),
|
|
1123
|
+
label=LabelStyle(
|
|
1124
|
+
font_size=24,
|
|
1125
|
+
font_color="#000",
|
|
1126
|
+
font_alpha=1,
|
|
1127
|
+
anchor_point=AnchorPointEnum.BOTTOM_CENTER,
|
|
1128
|
+
zorder=ZOrderEnum.LAYER_5 + 1000,
|
|
1129
|
+
),
|
|
1130
|
+
)
|
|
1131
|
+
"""Styling for gridlines (including Right Ascension / Declination labels). *Only applies to map plots*."""
|
|
1132
|
+
|
|
1133
|
+
ecliptic: PathStyle = PathStyle(
|
|
1134
|
+
line=LineStyle(
|
|
1135
|
+
color="#777",
|
|
1136
|
+
width=3,
|
|
1137
|
+
style=LineStyleEnum.DOTTED,
|
|
1138
|
+
dash_capstyle=CapStyleEnum.ROUND,
|
|
1139
|
+
alpha=1,
|
|
1140
|
+
zorder=ZOrderEnum.LAYER_3 - 1,
|
|
1141
|
+
),
|
|
1142
|
+
label=LabelStyle(
|
|
1143
|
+
font_size=21,
|
|
1144
|
+
font_color="#777",
|
|
1145
|
+
font_alpha=1,
|
|
1146
|
+
font_weight=FontWeightEnum.NORMAL,
|
|
1147
|
+
border_width=8,
|
|
1148
|
+
border_color="#000",
|
|
1149
|
+
zorder=ZOrderEnum.LAYER_3,
|
|
1150
|
+
),
|
|
1151
|
+
)
|
|
1152
|
+
"""Styling for the Ecliptic"""
|
|
1153
|
+
|
|
1154
|
+
celestial_equator: PathStyle = PathStyle(
|
|
1155
|
+
line=LineStyle(
|
|
1156
|
+
color="#999",
|
|
1157
|
+
width=3,
|
|
1158
|
+
style=LineStyleEnum.DASHED_DOTS,
|
|
1159
|
+
alpha=1,
|
|
1160
|
+
zorder=ZOrderEnum.LAYER_3,
|
|
1161
|
+
),
|
|
1162
|
+
label=LabelStyle(
|
|
1163
|
+
font_size=21,
|
|
1164
|
+
font_color="#999",
|
|
1165
|
+
font_weight=FontWeightEnum.NORMAL,
|
|
1166
|
+
font_alpha=1,
|
|
1167
|
+
border_width=8,
|
|
1168
|
+
border_color="#000",
|
|
1169
|
+
zorder=ZOrderEnum.LAYER_3,
|
|
1170
|
+
),
|
|
1171
|
+
)
|
|
1172
|
+
"""Styling for the Celestial Equator"""
|
|
1173
|
+
|
|
1174
|
+
galactic_equator: PathStyle = PathStyle(
|
|
1175
|
+
line=LineStyle(
|
|
1176
|
+
color="#999",
|
|
1177
|
+
width=3,
|
|
1178
|
+
style=LineStyleEnum.SOLID,
|
|
1179
|
+
alpha=0.65,
|
|
1180
|
+
zorder=ZOrderEnum.LAYER_3,
|
|
1181
|
+
),
|
|
1182
|
+
label=LabelStyle(
|
|
1183
|
+
font_size=21,
|
|
1184
|
+
font_color="#7c7c7c",
|
|
1185
|
+
font_weight=FontWeightEnum.NORMAL,
|
|
1186
|
+
font_alpha=1,
|
|
1187
|
+
border_width=8,
|
|
1188
|
+
border_color="#000",
|
|
1189
|
+
zorder=ZOrderEnum.LAYER_3,
|
|
1190
|
+
),
|
|
1191
|
+
)
|
|
1192
|
+
"""Styling for the Galactic Equator"""
|
|
1193
|
+
|
|
1194
|
+
horizon: PathStyle = PathStyle(
|
|
1195
|
+
line=LineStyle(
|
|
1196
|
+
color="#fff",
|
|
1197
|
+
width=80,
|
|
1198
|
+
edge_width=4,
|
|
1199
|
+
edge_color="#000",
|
|
1200
|
+
style=LineStyleEnum.SOLID,
|
|
1201
|
+
dash_capstyle=CapStyleEnum.BUTT,
|
|
1202
|
+
alpha=1,
|
|
1203
|
+
zorder=ZOrderEnum.LAYER_5,
|
|
1204
|
+
),
|
|
1205
|
+
label=LabelStyle(
|
|
1206
|
+
anchor_point=AnchorPointEnum.CENTER,
|
|
1207
|
+
font_color="#000",
|
|
1208
|
+
font_size=64,
|
|
1209
|
+
font_weight=FontWeightEnum.BOLD,
|
|
1210
|
+
zorder=ZOrderEnum.LAYER_5,
|
|
1211
|
+
),
|
|
1212
|
+
)
|
|
1213
|
+
"""Styling for the horizon"""
|
|
1214
|
+
|
|
1215
|
+
zenith: ObjectStyle = ObjectStyle(
|
|
1216
|
+
marker=MarkerStyle(
|
|
1217
|
+
symbol=MarkerSymbolEnum.TRIANGLE,
|
|
1218
|
+
size=24,
|
|
1219
|
+
fill=FillStyleEnum.FULL,
|
|
1220
|
+
color="#000",
|
|
1221
|
+
alpha=0.8,
|
|
1222
|
+
),
|
|
1223
|
+
label=LabelStyle(font_size=14, font_weight=FontWeightEnum.BOLD),
|
|
1224
|
+
)
|
|
1225
|
+
"""Styling for the zenith marker"""
|
|
1226
|
+
|
|
1227
|
+
optic_fov: PolygonStyle = PolygonStyle(
|
|
1228
|
+
fill_color=None,
|
|
1229
|
+
edge_color="red",
|
|
1230
|
+
line_style=[1, [2, 3]],
|
|
1231
|
+
edge_width=3,
|
|
1232
|
+
zorder=-1000,
|
|
1233
|
+
)
|
|
1234
|
+
"""Styling for optic fields of view"""
|
|
1235
|
+
|
|
1236
|
+
arrow: ArrowStyle = ArrowStyle(
|
|
1237
|
+
fill_color="hsl(0, 99%, 31%)",
|
|
1238
|
+
edge_color="#ff0019",
|
|
1239
|
+
edge_width=2,
|
|
1240
|
+
zorder=ZOrderEnum.LAYER_4,
|
|
1241
|
+
)
|
|
1242
|
+
"""Styling for optic fields of view"""
|
|
1243
|
+
|
|
1244
|
+
def get_dso_style(self, dso_type: DsoType):
|
|
1245
|
+
"""Returns the style for a DSO type"""
|
|
1246
|
+
styles_by_type = {
|
|
1247
|
+
# Star Clusters ----------
|
|
1248
|
+
DsoType.OPEN_CLUSTER: self.dso_open_cluster,
|
|
1249
|
+
DsoType.GLOBULAR_CLUSTER: self.dso_globular_cluster,
|
|
1250
|
+
# Galaxies ----------
|
|
1251
|
+
DsoType.GALAXY: self.dso_galaxy,
|
|
1252
|
+
DsoType.GALAXY_PAIR: self.dso_galaxy,
|
|
1253
|
+
DsoType.GALAXY_TRIPLET: self.dso_galaxy,
|
|
1254
|
+
DsoType.GROUP_OF_GALAXIES: self.dso_galaxy,
|
|
1255
|
+
# Nebulas ----------
|
|
1256
|
+
DsoType.NEBULA: self.dso_nebula,
|
|
1257
|
+
DsoType.PLANETARY_NEBULA: self.dso_planetary_nebula,
|
|
1258
|
+
DsoType.EMISSION_NEBULA: self.dso_nebula,
|
|
1259
|
+
DsoType.STAR_CLUSTER_NEBULA: self.dso_nebula,
|
|
1260
|
+
DsoType.REFLECTION_NEBULA: self.dso_nebula,
|
|
1261
|
+
DsoType.HII_IONIZED_REGION: self.dso_nebula,
|
|
1262
|
+
# Stars ----------
|
|
1263
|
+
DsoType.STAR: self.star,
|
|
1264
|
+
DsoType.DOUBLE_STAR: self.dso_double_star,
|
|
1265
|
+
DsoType.ASSOCIATION_OF_STARS: self.dso_association_stars,
|
|
1266
|
+
# Others ----------
|
|
1267
|
+
DsoType.DARK_NEBULA: self.dso_dark_nebula,
|
|
1268
|
+
DsoType.SUPERNOVA_REMNANT: self.dso_supernova_remnant,
|
|
1269
|
+
DsoType.NOVA_STAR: self.dso_nova_star,
|
|
1270
|
+
DsoType.NONEXISTENT: self.dso_nonexistant,
|
|
1271
|
+
DsoType.UNKNOWN: self.dso_unknown,
|
|
1272
|
+
DsoType.DUPLICATE_RECORD: self.dso_duplicate,
|
|
1273
|
+
}
|
|
1274
|
+
return styles_by_type.get(dso_type)
|
|
1275
|
+
|
|
1276
|
+
@staticmethod
|
|
1277
|
+
def load_from_file(filename: str) -> "PlotStyle":
|
|
1278
|
+
"""
|
|
1279
|
+
Load a style from a YAML file. The returned style is an extension of the default PlotStyle
|
|
1280
|
+
(see [`PlotStyle.extend`][starplot.styles.PlotStyle.extend]), so you only need to define
|
|
1281
|
+
properties you want to override from the default.
|
|
1282
|
+
|
|
1283
|
+
Args:
|
|
1284
|
+
filename: Filename of style file
|
|
1285
|
+
|
|
1286
|
+
Returns:
|
|
1287
|
+
PlotStyle: A new instance of a PlotStyle
|
|
1288
|
+
"""
|
|
1289
|
+
with open(filename, "r") as sfile:
|
|
1290
|
+
style = yaml.safe_load(sfile)
|
|
1291
|
+
return PlotStyle().extend(style)
|
|
1292
|
+
|
|
1293
|
+
def dump_to_file(self, filename: str) -> None:
|
|
1294
|
+
"""
|
|
1295
|
+
Save the style to a YAML file. ALL style properties will be written to the file.
|
|
1296
|
+
|
|
1297
|
+
Args:
|
|
1298
|
+
filename: Filename of style file
|
|
1299
|
+
"""
|
|
1300
|
+
with open(filename, "w") as outfile:
|
|
1301
|
+
style_json = self.model_dump_json()
|
|
1302
|
+
style_yaml = yaml.dump(json.loads(style_json))
|
|
1303
|
+
outfile.write(style_yaml)
|
|
1304
|
+
|
|
1305
|
+
def extend(self, *args, **kwargs) -> "PlotStyle":
|
|
1306
|
+
"""
|
|
1307
|
+
Adds one or more dicts of style overrides to the style and returns a new instance with
|
|
1308
|
+
those overrides.
|
|
1309
|
+
|
|
1310
|
+
Styles are added in sequential order, so if the first style arg has a property
|
|
1311
|
+
that is also in the last style arg, then the resulting style will have the value
|
|
1312
|
+
from the last style (similar to how CSS works).
|
|
1313
|
+
|
|
1314
|
+
???- tip "Example Usage"
|
|
1315
|
+
Create an extension of the default style with the light blue color scheme, map optimizations,
|
|
1316
|
+
and change the constellation line color to red:
|
|
1317
|
+
|
|
1318
|
+
```python
|
|
1319
|
+
|
|
1320
|
+
new_style = PlotStyle().extend(
|
|
1321
|
+
styles.extensions.BLUE_LIGHT,
|
|
1322
|
+
styles.extensions.MAP,
|
|
1323
|
+
{
|
|
1324
|
+
"constellation": {"line": {"color": "#e12d2d"}},
|
|
1325
|
+
},
|
|
1326
|
+
)
|
|
1327
|
+
```
|
|
1328
|
+
|
|
1329
|
+
Args:
|
|
1330
|
+
args: One or more dicts of styles to add
|
|
1331
|
+
|
|
1332
|
+
Returns:
|
|
1333
|
+
PlotStyle: A new instance of a PlotStyle
|
|
1334
|
+
"""
|
|
1335
|
+
style_json = self.model_dump_json()
|
|
1336
|
+
style_dict = json.loads(style_json)
|
|
1337
|
+
for a in args:
|
|
1338
|
+
if not isinstance(a, dict):
|
|
1339
|
+
raise TypeError("Style overrides must be dictionary types.")
|
|
1340
|
+
merge_dict(style_dict, a)
|
|
1341
|
+
return PlotStyle.parse_obj(style_dict)
|
|
1342
|
+
|
|
1343
|
+
def has_gradient_background(self):
|
|
1344
|
+
return isinstance(self.background_color, list)
|