starplot 0.16.4__py2.py3-none-any.whl → 0.17.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.
Files changed (37) hide show
  1. starplot/__init__.py +28 -2
  2. starplot/base.py +36 -56
  3. starplot/config.py +16 -0
  4. starplot/data/constellations.py +5 -676
  5. starplot/data/dsos.py +7 -23
  6. starplot/data/library/bigsky.0.4.0.stars.mag11.parquet +0 -0
  7. starplot/data/library/sky.db +0 -0
  8. starplot/data/stars.py +12 -2
  9. starplot/data/translations.py +161 -0
  10. starplot/geometry.py +7 -6
  11. starplot/horizon.py +9 -8
  12. starplot/map.py +1 -1
  13. starplot/models/__init__.py +19 -7
  14. starplot/models/base.py +1 -1
  15. starplot/models/comet.py +332 -0
  16. starplot/models/constellation.py +10 -0
  17. starplot/models/dso.py +24 -0
  18. starplot/{optics.py → models/optics.py} +4 -4
  19. starplot/models/satellite.py +158 -0
  20. starplot/models/star.py +10 -0
  21. starplot/optic.py +10 -9
  22. starplot/plotters/__init__.py +1 -0
  23. starplot/plotters/arrow.py +162 -0
  24. starplot/plotters/constellations.py +15 -51
  25. starplot/plotters/dsos.py +8 -14
  26. starplot/plotters/experimental.py +559 -6
  27. starplot/plotters/legend.py +5 -0
  28. starplot/plotters/stars.py +7 -16
  29. starplot/styles/base.py +20 -1
  30. starplot/styles/extensions.py +10 -1
  31. starplot/zenith.py +4 -1
  32. {starplot-0.16.4.dist-info → starplot-0.17.0.dist-info}/METADATA +19 -15
  33. {starplot-0.16.4.dist-info → starplot-0.17.0.dist-info}/RECORD +37 -33
  34. /starplot/{observer.py → models/observer.py} +0 -0
  35. {starplot-0.16.4.dist-info → starplot-0.17.0.dist-info}/WHEEL +0 -0
  36. {starplot-0.16.4.dist-info → starplot-0.17.0.dist-info}/entry_points.txt +0 -0
  37. {starplot-0.16.4.dist-info → starplot-0.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,162 @@
1
+ import numpy as np
2
+
3
+ from starplot.geometry import circle
4
+ from starplot.profile import profile
5
+
6
+
7
+ class ArrowPlotterMixin:
8
+ def _to_axes(self, points):
9
+ ax_points = []
10
+
11
+ for ra, dec in points:
12
+ x, y = self._proj.transform_point(ra, dec, self._crs)
13
+ data_to_axes = self.ax.transData + self.ax.transAxes.inverted()
14
+ x_axes, y_axes = data_to_axes.transform((x, y))
15
+ ax_points.append([x_axes, y_axes])
16
+ return ax_points
17
+
18
+ @profile
19
+ def arrow(
20
+ self,
21
+ origin: tuple[float, float] = None,
22
+ target: tuple[float, float] = None,
23
+ scale: float = 0.9,
24
+ length: float = 1,
25
+ ):
26
+ """
27
+ Plots an arrow from one point to another.
28
+
29
+ Args:
30
+ origin: Starting point (ra, dec)
31
+ target: Target of the arrow (ra, dec)
32
+ scale: Scaling factor for the arrow, to make it offset from the origin/target
33
+ length: If you only specify a target, then this will be the length of the arrow (in degrees). This value is ignored if you're plotting an arrow from one point to another.
34
+ style: Style of the arrow
35
+
36
+ TODO : add style kwarg
37
+
38
+ Draw as polygon instead
39
+ """
40
+ # self._text(x, y, labels[i], **text_kwargs)
41
+ # self.ax.annotate("", xytext=(0, 0), xy=(0.5, 0.5),
42
+ # )
43
+ # # y = mx + b
44
+ # def _slope(x1, y1, x2, y2):
45
+ # return (y2-y1)/(x2-x1)
46
+ # slope = _slope(x, y, target_x, target_y)
47
+ # b = y - slope * x
48
+
49
+ """
50
+ Do this in display coords
51
+
52
+ 1. Create LineString
53
+ 2. Segmentize
54
+ 3. Buffer
55
+ 4. Create arrow head (triangle)
56
+ """
57
+
58
+ if origin and target:
59
+ # TODO : prepare coords
60
+ ax_coords = self._to_axes([origin, target])
61
+ x, y = ax_coords[0]
62
+ target_x, target_y = ax_coords[1]
63
+
64
+ angle_radians = np.atan2(target_y - y, target_x - x)
65
+ angle_degrees = np.degrees(angle_radians)
66
+
67
+ line_x = [
68
+ (1 - scale) * ax_coords[1][0] + scale * ax_coords[0][0],
69
+ (1 - scale) * ax_coords[0][0] + scale * ax_coords[1][0],
70
+ ]
71
+ line_y = [
72
+ (1 - scale) * ax_coords[1][1] + scale * ax_coords[0][1],
73
+ (1 - scale) * ax_coords[0][1] + scale * ax_coords[1][1],
74
+ ]
75
+
76
+ # plot line
77
+ self.ax.plot(
78
+ line_x,
79
+ line_y,
80
+ clip_on=True,
81
+ clip_path=self._background_clip_path,
82
+ color="red",
83
+ linewidth=5,
84
+ transform=self.ax.transAxes,
85
+ )
86
+
87
+ arrow_x = line_x[-1]
88
+ arrow_y = line_y[-1]
89
+
90
+ # plot arrowhead
91
+ self.ax.plot(
92
+ arrow_x,
93
+ arrow_y,
94
+ marker=(3, 0, angle_degrees + 270),
95
+ markersize=30,
96
+ color="red",
97
+ linestyle="None",
98
+ transform=self.ax.transAxes,
99
+ )
100
+ elif target:
101
+ polygon = circle(
102
+ center=target,
103
+ diameter_degrees=length,
104
+ num_pts=200,
105
+ )
106
+
107
+ list(zip(*polygon.exterior.coords.xy))
108
+
109
+ """
110
+ TODO :
111
+ add support for target only:
112
+ length param (in degrees)
113
+ 1. create circle
114
+ 2. try random points on circle
115
+ 3. generate points on line from origin to target
116
+ 4. if points collide with label, try another random point
117
+ 5. stop when no collisions with labels
118
+ """
119
+
120
+ else:
121
+ raise ValueError(
122
+ "To plot an arrow you must specify a target or a target and origin."
123
+ )
124
+
125
+ # Convert to display coordinates
126
+ # display_x, display_y = ax.transAxes.transform((axes_x, axes_y))
127
+
128
+ # points = geod.ellipse(
129
+ # center=(),
130
+ # height_degrees,
131
+ # width_degrees,
132
+ # num_pts=200,
133
+ # )
134
+
135
+ # self.ax.arrow(
136
+ # x,
137
+ # y,
138
+ # target_x - x,
139
+ # target_y - y,
140
+ # color="red",
141
+ # head_length=1,
142
+ # head_width=1,
143
+ # transform=self.ax.transAxes,
144
+ # )
145
+
146
+ # ra, dec = to
147
+ # x, y = self._prepare_coords(ra, dec)
148
+
149
+ # self.ax.annotate(
150
+ # "",
151
+ # xy=(x, y),
152
+ # xytext=(0, 0),
153
+ # arrowprops=dict(
154
+ # # headlength=10,
155
+ # # headwidth=16,
156
+ # color="red",
157
+ # # shrink=0.9,
158
+ # mutation_scale=5,
159
+ # arrowstyle="-|>, head_length=10, head_width=5",
160
+ # ),
161
+ # **self._plot_kwargs(),
162
+ # )
@@ -1,3 +1,5 @@
1
+ from typing import Callable
2
+
1
3
  import numpy as np
2
4
 
3
5
  import rtree
@@ -10,11 +12,8 @@ from ibis import _
10
12
  from starplot.coordinates import CoordinateSystem
11
13
  from starplot.data import constellations as condata, constellation_lines as conlines
12
14
  from starplot.data.stars import load as load_stars, StarCatalog
13
- from starplot.data.constellations import (
14
- CONSTELLATIONS_FULL_NAMES,
15
- )
16
15
  from starplot.data.constellation_stars import CONSTELLATION_HIPS
17
- from starplot.models import Star
16
+ from starplot.models import Star, Constellation
18
17
  from starplot.models.constellation import from_tuple as constellation_from_tuple
19
18
  from starplot.projections import (
20
19
  StereoNorth,
@@ -24,7 +23,7 @@ from starplot.projections import (
24
23
  LambertAzEqArea,
25
24
  )
26
25
  from starplot.profile import profile
27
- from starplot.styles import PathStyle, LineStyle, LabelStyle
26
+ from starplot.styles import LineStyle, LabelStyle
28
27
  from starplot.styles.helpers import use_style
29
28
  from starplot.utils import points_on_line
30
29
  from starplot.geometry import is_wrapped_polygon
@@ -197,40 +196,6 @@ class ConstellationPlotterMixin:
197
196
  None,
198
197
  )
199
198
 
200
- def _plot_constellation_labels(
201
- self,
202
- style: PathStyle = None,
203
- labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
204
- ):
205
- """
206
- TODO:
207
- 1. plot label, if removed then get size in display coords
208
- 2. generate random points in polygon, convert to display coords, test for intersections
209
- 3. plot best score
210
-
211
- problem = constellations usually plotted first, so wont have star data (or could use stars from constellations only?)
212
-
213
- constellation names CAN cross lines but not stars
214
-
215
- """
216
- style = style or self.style.constellation.label
217
- self._constellation_labels = []
218
-
219
- for con in condata.iterator():
220
- _, ra, dec = condata.get(con)
221
- text = labels.get(con.lower())
222
- label = self.text(
223
- text,
224
- ra,
225
- dec,
226
- style,
227
- hide_on_collision=False,
228
- # hide_on_collision=self.hide_colliding_labels,
229
- gid="constellations-label-name",
230
- )
231
- if label is not None:
232
- self._constellation_labels.append(label)
233
-
234
199
  @profile
235
200
  @use_style(LineStyle, "constellation_borders")
236
201
  def constellation_borders(self, style: LineStyle = None):
@@ -275,7 +240,7 @@ class ConstellationPlotterMixin:
275
240
  )
276
241
  self.ax.add_collection(line_collection)
277
242
 
278
- def _constellation_labels_auto(self, style, labels, settings):
243
+ def _constellation_labels_auto(self, style, label_fn, settings):
279
244
  hips = []
280
245
  for c in self.objects.constellations:
281
246
  hips.extend(c.star_hip_ids)
@@ -303,7 +268,7 @@ class ConstellationPlotterMixin:
303
268
 
304
269
  points_line = MultiPoint(starpoints)
305
270
  centroid = points_line.centroid
306
- text = labels.get(constellation.iau_id)
271
+ text = label_fn(constellation)
307
272
 
308
273
  self.text(
309
274
  text,
@@ -316,14 +281,13 @@ class ConstellationPlotterMixin:
316
281
  gid="constellations-label-name",
317
282
  )
318
283
 
319
- def _constellation_labels_static(self, style, labels):
320
- for con in condata.iterator():
321
- _, ra, dec = condata.get(con)
322
- text = labels.get(con.lower())
284
+ def _constellation_labels_static(self, style, label_fn):
285
+ for constellation in self.objects.constellations:
286
+ text = label_fn(constellation)
323
287
  self.text(
324
288
  text,
325
- ra * 15,
326
- dec,
289
+ constellation.ra,
290
+ constellation.dec,
327
291
  style,
328
292
  hide_on_collision=self.hide_colliding_labels,
329
293
  remove_on_constellation_collision=False,
@@ -335,7 +299,7 @@ class ConstellationPlotterMixin:
335
299
  def constellation_labels(
336
300
  self,
337
301
  style: LabelStyle = None,
338
- labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
302
+ label_fn: Callable[[Constellation], str] = Constellation.get_label,
339
303
  auto_adjust: bool = True,
340
304
  auto_adjust_settings: dict = DEFAULT_AUTO_ADJUST_SETTINGS,
341
305
  ):
@@ -346,7 +310,7 @@ class ConstellationPlotterMixin:
346
310
 
347
311
  Args:
348
312
  style: Styling of the constellation labels. If None, then the plot's style (specified when creating the plot) will be used
349
- labels: A dictionary where the keys are each constellation's 3-letter IAU abbreviation, and the values are how the constellation will be labeled on the plot.
313
+ label_fn: Callable for determining the label for each constellation. The default function returns the constellation's name in uppercase.
350
314
  auto_adjust: If True (the default), then labels will be automatically adjusted to avoid collisions with other labels and stars **Important: you must plot stars and constellations first for this to work**. This uses a fairly simple method: for each constellation it finds the centroid of all plotted constellation stars with lines and then generates random points in the constellation boundary starting at the centroid and then progressively increasing the distance from the centroid.
351
315
  auto_adjust_settings: Optional settings for the auto adjustment algorithm.
352
316
  """
@@ -354,6 +318,6 @@ class ConstellationPlotterMixin:
354
318
  if auto_adjust:
355
319
  settings = DEFAULT_AUTO_ADJUST_SETTINGS
356
320
  settings.update(auto_adjust_settings)
357
- self._constellation_labels_auto(style, labels, settings=settings)
321
+ self._constellation_labels_auto(style, label_fn, settings=settings)
358
322
  else:
359
- self._constellation_labels_static(style, labels)
323
+ self._constellation_labels_static(style, label_fn)
starplot/plotters/dsos.py CHANGED
@@ -3,11 +3,8 @@ from typing import Callable, Mapping
3
3
  from ibis import _
4
4
  import numpy as np
5
5
 
6
- from starplot.data.dsos import (
7
- DSO_LABELS_DEFAULT,
8
- DsoLabelMaker,
9
- load,
10
- )
6
+ from starplot.data.dsos import load
7
+ from starplot.data.translations import translate
11
8
  from starplot.models.dso import (
12
9
  DSO,
13
10
  DsoType,
@@ -109,10 +106,9 @@ class DsoPlotterMixin:
109
106
  where: list = None,
110
107
  where_labels: list = None,
111
108
  true_size: bool = True,
112
- labels: Mapping[str, str] = DSO_LABELS_DEFAULT,
113
109
  legend_labels: Mapping[DsoType, str] = DSO_LEGEND_LABELS,
114
110
  alpha_fn: Callable[[DSO], float] = None,
115
- label_fn: Callable[[DSO], str] = None,
111
+ label_fn: Callable[[DSO], str] = DSO.get_label,
116
112
  sql: str = None,
117
113
  sql_labels: str = None,
118
114
  ):
@@ -126,7 +122,7 @@ class DsoPlotterMixin:
126
122
  labels: A dictionary that maps DSO names (as specified in OpenNGC) to the label that'll be plotted for that object. By default, the DSO's name in OpenNGC will be used as the label. If you want to hide all labels, then set this arg to `None`.
127
123
  legend_labels: A dictionary that maps a `DsoType` to the legend label that'll be plotted for that type of DSO. If you want to hide all DSO legend labels, then set this arg to `None`.
128
124
  alpha_fn: Callable for calculating the alpha value (aka "opacity") of each DSO. If `None`, then the marker style's alpha will be used.
129
- label_fn: Callable for determining the label of each DSO. If `None`, then the names in the `labels` kwarg will be used.
125
+ label_fn: Callable for determining the label of each DSO.
130
126
  sql: SQL query for selecting DSOs (table name is `_`). This query will be applied _after_ any filters in the `where` kwarg.
131
127
  sql_labels: SQL query for selecting DSOs that will be labeled (table name is `_`). Applied _after_ any filters in the `where_labels` kwarg.
132
128
  """
@@ -136,11 +132,6 @@ class DsoPlotterMixin:
136
132
  where = where or []
137
133
  where_labels = where_labels or []
138
134
 
139
- if labels is None:
140
- labels = {}
141
- elif type(labels) != DsoLabelMaker:
142
- labels = DsoLabelMaker(overrides=labels)
143
-
144
135
  if legend_labels is None:
145
136
  legend_labels = {}
146
137
  else:
@@ -172,8 +163,11 @@ class DsoPlotterMixin:
172
163
  style = self.style.get_dso_style(dso_type)
173
164
  maj_ax, min_ax, angle = d.maj_ax, d.min_ax, d.angle
174
165
  legend_label = legend_labels.get(dso_type)
166
+ if legend_label:
167
+ legend_label = translate(legend_label, self.language) or legend_label
175
168
  _dso = from_tuple(d)
176
- label = labels.get(d.name) if label_fn is None else label_fn(_dso)
169
+
170
+ label = label_fn(_dso)
177
171
 
178
172
  if style is None:
179
173
  continue