starplot 0.13.0__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.

@@ -0,0 +1,339 @@
1
+ import geopandas as gpd
2
+ import numpy as np
3
+
4
+ import rtree
5
+ from shapely import (
6
+ MultiPoint,
7
+ )
8
+ from matplotlib.collections import LineCollection
9
+
10
+ from starplot.coordinates import CoordinateSystem
11
+ from starplot.data import DataFiles, constellations as condata, stars
12
+ from starplot.data.constellations import (
13
+ CONSTELLATIONS_FULL_NAMES,
14
+ CONSTELLATION_HIP_IDS,
15
+ )
16
+ from starplot.models.constellation import from_tuple as constellation_from_tuple
17
+ from starplot.projections import Projection
18
+ from starplot.styles import PathStyle, LineStyle, LabelStyle
19
+ from starplot.styles.helpers import use_style
20
+ from starplot.utils import points_on_line
21
+ from starplot.geometry import wrapped_polygon_adjustment
22
+
23
+ DEFAULT_AUTO_ADJUST_SETTINGS = {
24
+ "avoid_constellation_lines": False,
25
+ "point_generation_max_iterations": 500,
26
+ "distance_step_size": 1,
27
+ "max_distance": 300,
28
+ "label_padding": 9,
29
+ "buffer": 0.05,
30
+ "seed": None,
31
+ }
32
+ """Default settings for auto-adjusting constellation labels"""
33
+
34
+
35
+ class ConstellationPlotterMixin:
36
+ @use_style(LineStyle, "constellation_lines")
37
+ def constellations(
38
+ self,
39
+ style: LineStyle = None,
40
+ where: list = None,
41
+ ):
42
+ """Plots the constellation lines **only**. To plot constellation borders and/or labels, see separate functions for them.
43
+
44
+ **Important:** If you're plotting the constellation lines, then it's good to plot them _first_, because Starplot will use the constellation lines to determine where to place labels that are plotted afterwards (labels will look better if they're not crossing a constellation line).
45
+
46
+ Args:
47
+ style: Styling of the constellations. If None, then the plot's style (specified when creating the plot) will be used
48
+ where: A list of expressions that determine which constellations to plot. See [Selecting Objects](/reference-selecting-objects/) for details.
49
+ """
50
+ self.logger.debug("Plotting constellation lines...")
51
+
52
+ where = where or []
53
+ ctr = 0
54
+
55
+ constellations_gdf = gpd.read_file(
56
+ DataFiles.CONSTELLATIONS.value,
57
+ engine="pyogrio",
58
+ use_arrow=True,
59
+ bbox=self._extent_mask(),
60
+ )
61
+ stars_df = stars.load("hipparcos")
62
+
63
+ if constellations_gdf.empty:
64
+ return
65
+
66
+ if getattr(self, "projection", None) in [
67
+ Projection.MERCATOR,
68
+ Projection.MILLER,
69
+ ]:
70
+ transform = self._plate_carree
71
+ else:
72
+ transform = self._geodetic
73
+
74
+ conline_hips = condata.lines()
75
+ style_kwargs = style.matplot_kwargs(self.scale)
76
+ constellation_points_to_index = []
77
+ lines = []
78
+
79
+ for c in constellations_gdf.itertuples():
80
+ obj = constellation_from_tuple(c)
81
+
82
+ if not all([e.evaluate(obj) for e in where]):
83
+ continue
84
+
85
+ hiplines = conline_hips[c.iau_id]
86
+ inbounds = False
87
+
88
+ for s1_hip, s2_hip in hiplines:
89
+ s1 = stars_df.loc[s1_hip]
90
+ s2 = stars_df.loc[s2_hip]
91
+
92
+ s1_ra = s1.ra_hours * 15
93
+ s2_ra = s2.ra_hours * 15
94
+
95
+ s1_dec = s1.dec_degrees
96
+ s2_dec = s2.dec_degrees
97
+
98
+ if s1_ra - s2_ra > 60:
99
+ s2_ra += 360
100
+
101
+ elif s2_ra - s1_ra > 60:
102
+ s1_ra += 360
103
+
104
+ if not inbounds and self.in_bounds(s1.ra_hours, s1_dec):
105
+ inbounds = True
106
+
107
+ if self._coordinate_system == CoordinateSystem.RA_DEC:
108
+ s1_ra *= -1
109
+ s2_ra *= -1
110
+ x1, x2 = s1_ra, s2_ra
111
+ y1, y2 = s1_dec, s2_dec
112
+ elif self._coordinate_system == CoordinateSystem.AZ_ALT:
113
+ x1, y1 = self._prepare_coords(s1_ra / 15, s1_dec)
114
+ x2, y2 = self._prepare_coords(s2_ra / 15, s2_dec)
115
+ else:
116
+ raise ValueError("Unrecognized coordinate system")
117
+
118
+ lines.append([(x1, y1), (x2, y2)])
119
+
120
+ start = self._proj.transform_point(x1, y1, self._geodetic)
121
+ end = self._proj.transform_point(x2, y2, self._geodetic)
122
+ radius = style.width or 1
123
+
124
+ if any([np.isnan(n) for n in start + end]):
125
+ continue
126
+
127
+ for x, y in points_on_line(start, end, 25):
128
+ display_x, display_y = self.ax.transData.transform((x, y))
129
+ if display_x < 0 or display_y < 0:
130
+ continue
131
+ constellation_points_to_index.append(
132
+ (
133
+ ctr,
134
+ (
135
+ display_x - radius,
136
+ display_y - radius,
137
+ display_x + radius,
138
+ display_y + radius,
139
+ ),
140
+ None,
141
+ )
142
+ )
143
+ ctr += 1
144
+
145
+ if inbounds:
146
+ self._objects.constellations.append(obj)
147
+
148
+ style_kwargs = style.matplot_line_collection_kwargs(self.scale)
149
+
150
+ line_collection = LineCollection(
151
+ lines,
152
+ **style_kwargs,
153
+ transform=transform,
154
+ clip_on=True,
155
+ clip_path=self._background_clip_path,
156
+ gid="constellations-line",
157
+ )
158
+
159
+ self.ax.add_collection(line_collection)
160
+
161
+ if self._constellations_rtree.get_size() == 0:
162
+ self._constellations_rtree = rtree.index.Index(
163
+ constellation_points_to_index
164
+ )
165
+ else:
166
+ for bbox in constellation_points_to_index:
167
+ self._constellations_rtree.insert(
168
+ 0,
169
+ bbox,
170
+ None,
171
+ )
172
+ # self._plot_constellation_labels(style.label, labels_to_plot)
173
+ # self._plot_constellation_labels_experimental(style.label, labels_to_plot)
174
+
175
+ def _plot_constellation_labels(
176
+ self,
177
+ style: PathStyle = None,
178
+ labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
179
+ ):
180
+ """
181
+ TODO:
182
+ 1. plot label, if removed then get size in display coords
183
+ 2. generate random points in polygon, convert to display coords, test for intersections
184
+ 3. plot best score
185
+
186
+ problem = constellations usually plotted first, so wont have star data (or could use stars from constellations only?)
187
+
188
+ constellation names CAN cross lines but not stars
189
+
190
+ """
191
+ style = style or self.style.constellation.label
192
+ self._constellation_labels = []
193
+
194
+ for con in condata.iterator():
195
+ _, ra, dec = condata.get(con)
196
+ text = labels.get(con.lower())
197
+ label = self.text(
198
+ text,
199
+ ra,
200
+ dec,
201
+ style,
202
+ hide_on_collision=False,
203
+ # hide_on_collision=self.hide_colliding_labels,
204
+ gid="constellations-label-name",
205
+ )
206
+ if label is not None:
207
+ self._constellation_labels.append(label)
208
+
209
+ @use_style(LineStyle, "constellation_borders")
210
+ def constellation_borders(self, style: LineStyle = None):
211
+ """Plots the constellation borders
212
+
213
+ Args:
214
+ style: Styling of the constellation borders. If None, then the plot's style (specified when creating the plot) will be used
215
+ """
216
+ constellation_borders = self._read_geo_package(
217
+ DataFiles.CONSTELLATION_BORDERS.value
218
+ )
219
+
220
+ if constellation_borders.empty:
221
+ return
222
+
223
+ geometries = []
224
+ border_lines = []
225
+ transform = self._plate_carree
226
+
227
+ for _, c in constellation_borders.iterrows():
228
+ for ls in c.geometry.geoms:
229
+ geometries.append(ls)
230
+
231
+ for ls in geometries:
232
+ x, y = ls.xy
233
+ x = list(x)
234
+ y = list(y)
235
+
236
+ if self._coordinate_system == CoordinateSystem.RA_DEC:
237
+ border_lines.append(list(zip(x, y)))
238
+
239
+ elif self._coordinate_system == CoordinateSystem.AZ_ALT:
240
+ x = [24 - (x0 / 15) for x0 in x]
241
+ coords = [self._prepare_coords(*p) for p in list(zip(x, y))]
242
+ border_lines.append(coords)
243
+ transform = self._crs
244
+
245
+ else:
246
+ raise ValueError("Unrecognized coordinate system")
247
+
248
+ style_kwargs = style.matplot_line_collection_kwargs(self.scale)
249
+
250
+ line_collection = LineCollection(
251
+ border_lines,
252
+ **style_kwargs,
253
+ transform=transform,
254
+ clip_on=True,
255
+ clip_path=self._background_clip_path,
256
+ gid="constellations-border",
257
+ )
258
+
259
+ self.ax.add_collection(line_collection)
260
+
261
+ def _constellation_labels_auto(self, style, labels, settings):
262
+ for constellation in self.objects.constellations:
263
+ constellation_line_stars = [
264
+ s
265
+ for s in self.objects.stars
266
+ if s.hip in CONSTELLATION_HIP_IDS[constellation.iau_id]
267
+ ]
268
+ if not constellation_line_stars:
269
+ continue
270
+
271
+ points_line = MultiPoint([(s.ra, s.dec) for s in constellation_line_stars])
272
+ centroid = points_line.centroid
273
+
274
+ adjustment = wrapped_polygon_adjustment(constellation.boundary)
275
+
276
+ if (adjustment > 0 and centroid.x < 12) or (
277
+ adjustment < 0 and centroid.x > 12
278
+ ):
279
+ x = centroid.x + adjustment
280
+ else:
281
+ x = centroid.x
282
+
283
+ text = labels.get(constellation.iau_id)
284
+
285
+ self.text(
286
+ text,
287
+ x,
288
+ centroid.y,
289
+ style,
290
+ hide_on_collision=self.hide_colliding_labels,
291
+ area=constellation.boundary, # TODO : make this intersection with clip path
292
+ auto_adjust_settings=settings,
293
+ gid="constellations-label-name",
294
+ )
295
+
296
+ def _constellation_labels_static(self, style, labels):
297
+ for con in condata.iterator():
298
+ _, ra, dec = condata.get(con)
299
+ text = labels.get(con.lower())
300
+ self.text(
301
+ text,
302
+ ra,
303
+ dec,
304
+ style,
305
+ hide_on_collision=self.hide_colliding_labels,
306
+ gid="constellations-label-name",
307
+ )
308
+
309
+ @use_style(LabelStyle, "constellation_labels")
310
+ def constellation_labels(
311
+ self,
312
+ style: LabelStyle = None,
313
+ labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
314
+ auto_adjust: bool = True,
315
+ auto_adjust_settings: dict = DEFAULT_AUTO_ADJUST_SETTINGS,
316
+ ):
317
+ """
318
+ Plots constellation labels.
319
+
320
+ It's good to plot these last because they're area-based labels (vs point-based, like for star names), and area-based labels have more freedom to move around. If you plot area-based labels first, then it would limit the available space for point-based labels.
321
+
322
+ Args:
323
+ style: Styling of the constellation labels. If None, then the plot's style (specified when creating the plot) will be used
324
+ 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.
325
+ 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.
326
+ auto_adjust_settings: Optional settings for the auto adjustment algorithm.
327
+
328
+ TODO:
329
+ make this work without plotting constellations first
330
+
331
+ """
332
+ self.logger.debug("Plotting constellation labels...")
333
+
334
+ if auto_adjust:
335
+ settings = DEFAULT_AUTO_ADJUST_SETTINGS
336
+ settings.update(auto_adjust_settings)
337
+ self._constellation_labels_auto(style, labels, settings=settings)
338
+ else:
339
+ self._constellation_labels_static(style, labels)
@@ -0,0 +1,171 @@
1
+ import geopandas as gpd
2
+
3
+ from shapely import MultiPolygon
4
+ from shapely import (
5
+ MultiPoint,
6
+ intersection,
7
+ delaunay_triangles,
8
+ distance,
9
+ )
10
+
11
+ from starplot.data import DataFiles
12
+ from starplot.data.constellations import (
13
+ CONSTELLATIONS_FULL_NAMES,
14
+ CONSTELLATION_HIP_IDS,
15
+ )
16
+ from starplot.styles import PathStyle
17
+
18
+
19
+ class ExperimentalPlotterMixin:
20
+ def _constellation_borders(self):
21
+ from shapely import LineString, MultiLineString
22
+ from shapely.ops import unary_union
23
+
24
+ constellation_borders = gpd.read_file(
25
+ DataFiles.CONSTELLATIONS.value,
26
+ engine="pyogrio",
27
+ use_arrow=True,
28
+ bbox=self._extent_mask(),
29
+ )
30
+
31
+ if constellation_borders.empty:
32
+ return
33
+
34
+ geometries = []
35
+
36
+ for i, constellation in constellation_borders.iterrows():
37
+ geometry_types = constellation.geometry.geom_type
38
+
39
+ # equinox = LineString([[0, 90], [0, -90]])
40
+ """
41
+ Problems:
42
+ - Need to handle multipolygon borders too (SER)
43
+ - Shapely's union doesn't handle geodesy (e.g. TRI + AND)
44
+ - ^^ TRI is plotted with ra < 360, but AND has ra > 360
45
+ - ^^ idea: create union first and then remove duplicate lines?
46
+
47
+ TODO: create new static data file of constellation border lines
48
+ """
49
+
50
+ if "Polygon" in geometry_types and "MultiPolygon" not in geometry_types:
51
+ polygons = [constellation.geometry]
52
+
53
+ elif "MultiPolygon" in geometry_types:
54
+ polygons = constellation.geometry.geoms
55
+
56
+ for p in polygons:
57
+ coords = list(zip(*p.exterior.coords.xy))
58
+ # coords = [(ra * -1, dec) for ra, dec in coords]
59
+
60
+ new_coords = []
61
+
62
+ for i, c in enumerate(coords):
63
+ ra, dec = c
64
+ if i > 0:
65
+ if new_coords[i - 1][0] - ra > 60:
66
+ ra += 360
67
+
68
+ elif ra - new_coords[i - 1][0] > 60:
69
+ new_coords[i - 1][0] += 360
70
+
71
+ new_coords.append([ra, dec])
72
+
73
+ ls = LineString(new_coords)
74
+ geometries.append(ls)
75
+
76
+ mls = MultiLineString(geometries)
77
+ geometries = unary_union(mls)
78
+
79
+ for ls in list(geometries.geoms):
80
+ x, y = ls.xy
81
+
82
+ self.line(zip(x, y), self.style.constellation_borders)
83
+ # x, y = ls.xy
84
+ # newx = [xx * -1 for xx in list(x)]
85
+ # self.ax.plot(
86
+ # # list(x),
87
+ # newx,
88
+ # list(y),
89
+ # # **self._plot_kwargs(),
90
+ # # transform=self._geodetic,
91
+ # transform=self._plate_carree,
92
+ # **style_kwargs,
93
+ # )
94
+
95
+ def _plot_constellation_labels_experimental(
96
+ self,
97
+ style: PathStyle = None,
98
+ labels: dict[str, str] = CONSTELLATIONS_FULL_NAMES,
99
+ ):
100
+ def sorter(g):
101
+ # higher score is better
102
+ d = distance(g.centroid, points_line.centroid)
103
+ # d = distance(g.centroid, constellation.boundary.centroid)
104
+ extent = abs(g.bounds[2] - g.bounds[0])
105
+ area = g.area / constellation.boundary.area
106
+ # return ((extent**3)) * area**2
107
+ # return ((extent**2) - (d/2)) * area**2
108
+ # print(str(extent) + " " + str(area) + " " + str(d))
109
+ return d**2 * -1
110
+ return (extent / 2 + area) - (d / 5)
111
+
112
+ for constellation in self.objects.constellations:
113
+ constellation_stars = [
114
+ s
115
+ for s in self.objects.stars
116
+ if s.constellation_id == constellation.iau_id and s.magnitude < 5
117
+ ]
118
+ constellation_line_stars = [
119
+ s
120
+ for s in self.objects.stars
121
+ if s.constellation_id == constellation.iau_id
122
+ and s.hip in CONSTELLATION_HIP_IDS[constellation.iau_id]
123
+ ]
124
+ points = MultiPoint([(s.ra, s.dec) for s in constellation_stars])
125
+ points_line = MultiPoint([(s.ra, s.dec) for s in constellation_line_stars])
126
+
127
+ triangles = delaunay_triangles(
128
+ geometry=points,
129
+ tolerance=2,
130
+ )
131
+ print(constellation.name + " " + str(len(triangles.geoms)))
132
+
133
+ polygons = []
134
+ for t in triangles.geoms:
135
+ try:
136
+ inter = intersection(t, constellation.boundary)
137
+ except Exception:
138
+ continue
139
+ if (
140
+ inter.geom_type == "Polygon"
141
+ and len(list(zip(*inter.exterior.coords.xy))) > 2
142
+ ):
143
+ polygons.append(inter)
144
+
145
+ p_by_area = {pg.area: pg for pg in polygons}
146
+ polygons_sorted = [
147
+ p_by_area[k] for k in sorted(p_by_area.keys(), reverse=True)
148
+ ]
149
+
150
+ # sort by combination of horizontal extent and area
151
+ polygons_sorted = sorted(polygons_sorted, key=sorter, reverse=True)
152
+
153
+ if len(polygons_sorted) > 0:
154
+ i = 0
155
+ ra, dec = polygons_sorted[i].centroid.x, polygons_sorted[i].centroid.y
156
+ else:
157
+ ra, dec = constellation.ra, constellation.dec
158
+
159
+ text = labels.get(constellation.iau_id)
160
+ style = style or self.style.constellation.label
161
+ style.anchor_point = "center"
162
+ self.text(
163
+ text,
164
+ ra,
165
+ dec,
166
+ style,
167
+ hide_on_collision=self.hide_colliding_labels,
168
+ area=MultiPolygon(polygons_sorted[:3])
169
+ if len(polygons_sorted)
170
+ else constellation.boundary,
171
+ )
@@ -0,0 +1,41 @@
1
+ from starplot.data import DataFiles
2
+ from starplot.styles import PolygonStyle
3
+ from starplot.styles.helpers import use_style
4
+ from starplot.utils import lon_to_ra
5
+
6
+
7
+ class MilkyWayPlotterMixin:
8
+ @use_style(PolygonStyle, "milky_way")
9
+ def milky_way(self, style: PolygonStyle = None):
10
+ """
11
+ Plots the Milky Way
12
+
13
+ Args:
14
+ style: Styling of the Milky Way. If None, then the plot's style (specified when creating the plot) will be used
15
+ """
16
+
17
+ mw = self._read_geo_package(DataFiles.MILKY_WAY.value)
18
+
19
+ if mw.empty:
20
+ return
21
+
22
+ def _prepare_polygon(p):
23
+ points = list(zip(*p.boundary.coords.xy))
24
+ # convert lon to RA and reverse so the coordinates are counterclockwise order
25
+ return [(lon_to_ra(lon) * 15, dec) for lon, dec in reversed(points)]
26
+
27
+ # create union of all Milky Way patches
28
+ gs = mw.geometry.to_crs(self._plate_carree)
29
+ mw_union = gs.buffer(0.1).unary_union.buffer(-0.1)
30
+ polygons = []
31
+
32
+ if mw_union.geom_type == "MultiPolygon":
33
+ polygons.extend([_prepare_polygon(polygon) for polygon in mw_union.geoms])
34
+ else:
35
+ polygons.append(_prepare_polygon(mw_union))
36
+
37
+ for polygon_points in polygons:
38
+ self._polygon(
39
+ points=polygon_points,
40
+ style=style,
41
+ )
@@ -1,11 +1,13 @@
1
1
  from typing import Callable, Mapping
2
2
  from functools import cache
3
3
 
4
+ import rtree
4
5
  from skyfield.api import Star as SkyfieldStar
5
6
 
6
- # import numpy as np
7
+ import numpy as np
7
8
 
8
9
  from starplot import callables
10
+ from starplot.coordinates import CoordinateSystem
9
11
  from starplot.data import bayer, stars, flamsteed
10
12
  from starplot.data.stars import StarCatalog, STAR_NAMES
11
13
  from starplot.models.star import Star, from_tuple
@@ -117,8 +119,6 @@ class StarPlotterMixin:
117
119
  label,
118
120
  s.ra,
119
121
  s.dec,
120
- # style,
121
- # _offset(style, star_sizes[i]),
122
122
  style=style.label.offset_from_marker(
123
123
  marker_symbol=style.marker.symbol,
124
124
  marker_size=star_sizes[i],
@@ -222,6 +222,7 @@ class StarPlotterMixin:
222
222
  color_fn = color_fn or (lambda d: color_hex)
223
223
 
224
224
  where = where or []
225
+ stars_to_index = []
225
226
 
226
227
  if where:
227
228
  mag = None
@@ -242,42 +243,67 @@ class StarPlotterMixin:
242
243
  self._prepare_star_coords(nearby_stars_df)
243
244
 
244
245
  starz = []
246
+ ctr = 0
245
247
 
246
248
  for star in nearby_stars_df.itertuples():
247
249
  obj = from_tuple(star, catalog)
250
+ ctr += 1
248
251
 
249
252
  if not all([e.evaluate(obj) for e in where]):
250
253
  continue
251
254
 
255
+ if self._coordinate_system == CoordinateSystem.RA_DEC:
256
+ data_xy = self._proj.transform_point(
257
+ star.ra * -1, star.dec, self._geodetic
258
+ )
259
+ elif self._coordinate_system == CoordinateSystem.AZ_ALT:
260
+ data_xy = self._proj.transform_point(star.x, star.y, self._crs)
261
+ else:
262
+ raise ValueError("Unrecognized coordinate system")
263
+
264
+ display_x, display_y = self.ax.transData.transform(data_xy)
265
+
266
+ if (
267
+ display_x < 0
268
+ or display_y < 0
269
+ or np.isnan(display_x)
270
+ or np.isnan(display_y)
271
+ or self._is_clipped([(display_x, display_y)])
272
+ ):
273
+ continue
274
+
252
275
  size = size_fn(obj) * self.scale**2
253
276
  alpha = alpha_fn(obj)
254
277
  color = color_fn(obj) or style.marker.color.as_hex()
255
278
 
256
- starz.append((star.x, star.y, size, alpha, color, obj))
279
+ if obj.magnitude < 5:
280
+ # radius = ((size**0.5 / 2) / self.scale) #/ 3.14
281
+ radius = size**0.5 / 5
282
+
283
+ bbox = np.array(
284
+ (
285
+ display_x - radius,
286
+ display_y - radius,
287
+ display_x + radius,
288
+ display_y + radius,
289
+ )
290
+ )
291
+ # if obj.name == "Sirius":
292
+ # print(bbox)
293
+ # if obj.name == "Electra":
294
+ # print(bbox)
295
+
296
+ if self._stars_rtree.get_size() > 0:
297
+ self._stars_rtree.insert(
298
+ 0,
299
+ bbox,
300
+ None,
301
+ )
302
+ else:
303
+ # if the index has no stars yet, then wait until end to load for better performance
304
+ stars_to_index.append((ctr, bbox, None))
257
305
 
258
- # Experimental code for keeping spatial index of plotted stars (for better label placement)
259
- # if getattr(self, "_geodetic", None):
260
- # # TODO : clean up!
261
- # x, y = self._proj.transform_point(
262
- # star.ra * -1, star.dec, self._geodetic
263
- # )
264
- # x0, y0 = self.ax.transData.transform((x, y))
265
-
266
- # if (
267
- # x0 < 0
268
- # or y0 < 0
269
- # or obj.magnitude > 5
270
- # or np.isnan(x0)
271
- # or np.isnan(y0)
272
- # ):
273
- # continue
274
- # radius = 1 + (5 - obj.magnitude)
275
- # # radius = max(((size**0.5 / 2) / self.scale)/1.44 - 6, 0) #size / self.scale**2 / 200
276
- # self._stars_rtree.insert(
277
- # 0,
278
- # np.array((x0 - radius, y0 - radius, x0 + radius, y0 + radius)),
279
- # obj=star.x,
280
- # )
306
+ starz.append((star.x, star.y, size, alpha, color, obj))
281
307
 
282
308
  starz.sort(key=lambda s: s[2], reverse=True) # sort by descending size
283
309
 
@@ -308,6 +334,9 @@ class StarPlotterMixin:
308
334
 
309
335
  self._add_legend_handle_marker(legend_label, style.marker)
310
336
 
337
+ if stars_to_index:
338
+ self._stars_rtree = rtree.index.Index(stars_to_index)
339
+
311
340
  if labels:
312
341
  self._star_labels(
313
342
  star_objects,