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.

Files changed (73) hide show
  1. starplot/__init__.py +3 -2
  2. starplot/base.py +408 -95
  3. starplot/callables.py +61 -7
  4. starplot/coordinates.py +6 -0
  5. starplot/data/bayer.py +1532 -3
  6. starplot/data/constellations.py +564 -2
  7. starplot/data/flamsteed.py +2682 -0
  8. starplot/data/library/constellation_borders_inv.gpkg +0 -0
  9. starplot/data/library/constellation_lines_hips.json +3 -1
  10. starplot/data/stars.py +408 -87
  11. starplot/geometry.py +82 -0
  12. starplot/horizon.py +458 -0
  13. starplot/map.py +97 -284
  14. starplot/models/base.py +9 -2
  15. starplot/models/constellation.py +1 -1
  16. starplot/optic.py +32 -14
  17. starplot/plotters/__init__.py +2 -0
  18. starplot/plotters/constellations.py +339 -0
  19. starplot/plotters/dsos.py +5 -1
  20. starplot/plotters/experimental.py +171 -0
  21. starplot/plotters/milkyway.py +41 -0
  22. starplot/plotters/stars.py +143 -13
  23. starplot/styles/base.py +308 -169
  24. starplot/styles/ext/antique.yml +54 -46
  25. starplot/styles/ext/blue_dark.yml +39 -45
  26. starplot/styles/ext/blue_light.yml +49 -30
  27. starplot/styles/ext/blue_medium.yml +53 -50
  28. starplot/styles/ext/cb_wong.yml +16 -7
  29. starplot/styles/ext/grayscale.yml +17 -10
  30. starplot/styles/ext/grayscale_dark.yml +18 -8
  31. starplot/styles/ext/map.yml +10 -7
  32. starplot/styles/ext/nord.yml +38 -38
  33. starplot/styles/ext/optic.yml +7 -5
  34. starplot/styles/fonts-library/gfs-didot/DESCRIPTION.en_us.html +9 -0
  35. starplot/styles/fonts-library/gfs-didot/GFSDidot-Regular.ttf +0 -0
  36. starplot/styles/fonts-library/gfs-didot/METADATA.pb +16 -0
  37. starplot/styles/fonts-library/gfs-didot/OFL.txt +94 -0
  38. starplot/styles/fonts-library/hind/DESCRIPTION.en_us.html +28 -0
  39. starplot/styles/fonts-library/hind/Hind-Bold.ttf +0 -0
  40. starplot/styles/fonts-library/hind/Hind-Light.ttf +0 -0
  41. starplot/styles/fonts-library/hind/Hind-Medium.ttf +0 -0
  42. starplot/styles/fonts-library/hind/Hind-Regular.ttf +0 -0
  43. starplot/styles/fonts-library/hind/Hind-SemiBold.ttf +0 -0
  44. starplot/styles/fonts-library/hind/METADATA.pb +58 -0
  45. starplot/styles/fonts-library/hind/OFL.txt +93 -0
  46. starplot/styles/fonts-library/inter/Inter-Black.ttf +0 -0
  47. starplot/styles/fonts-library/inter/Inter-BlackItalic.ttf +0 -0
  48. starplot/styles/fonts-library/inter/Inter-Bold.ttf +0 -0
  49. starplot/styles/fonts-library/inter/Inter-BoldItalic.ttf +0 -0
  50. starplot/styles/fonts-library/inter/Inter-ExtraBold.ttf +0 -0
  51. starplot/styles/fonts-library/inter/Inter-ExtraBoldItalic.ttf +0 -0
  52. starplot/styles/fonts-library/inter/Inter-ExtraLight.ttf +0 -0
  53. starplot/styles/fonts-library/inter/Inter-ExtraLightItalic.ttf +0 -0
  54. starplot/styles/fonts-library/inter/Inter-Italic.ttf +0 -0
  55. starplot/styles/fonts-library/inter/Inter-Light.ttf +0 -0
  56. starplot/styles/fonts-library/inter/Inter-LightItalic.ttf +0 -0
  57. starplot/styles/fonts-library/inter/Inter-Medium.ttf +0 -0
  58. starplot/styles/fonts-library/inter/Inter-MediumItalic.ttf +0 -0
  59. starplot/styles/fonts-library/inter/Inter-Regular.ttf +0 -0
  60. starplot/styles/fonts-library/inter/Inter-SemiBold.ttf +0 -0
  61. starplot/styles/fonts-library/inter/Inter-SemiBoldItalic.ttf +0 -0
  62. starplot/styles/fonts-library/inter/Inter-Thin.ttf +0 -0
  63. starplot/styles/fonts-library/inter/Inter-ThinItalic.ttf +0 -0
  64. starplot/styles/fonts-library/inter/LICENSE.txt +92 -0
  65. starplot/styles/fonts.py +15 -0
  66. starplot/styles/markers.py +207 -6
  67. starplot/utils.py +19 -0
  68. starplot/warnings.py +16 -0
  69. {starplot-0.12.5.dist-info → starplot-0.14.0.dist-info}/METADATA +12 -12
  70. starplot-0.14.0.dist-info/RECORD +107 -0
  71. starplot-0.12.5.dist-info/RECORD +0 -67
  72. {starplot-0.12.5.dist-info → starplot-0.14.0.dist-info}/LICENSE +0 -0
  73. {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 LineString, MultiLineString, Polygon
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.models.constellation import from_tuple as constellation_from_tuple
22
- from starplot.plotters import StarPlotterMixin, DsoPlotterMixin
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
- class MapPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
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 = 2048,
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 because
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._size_multiplier)
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
- patch = patches.Polygon(
562
- verts,
346
+ patch = patches.Circle(
347
+ (0.50, 0.50),
348
+ radius=0.454,
563
349
  facecolor=None,
564
350
  fill=False,
565
- transform=self._crs,
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._size_multiplier),
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._size_multiplier),
400
+ **style.label.matplot_kwargs(self.scale),
601
401
  hide_on_collision=False,
602
- xytext=(style.label.offset_x, style.label.offset_y),
603
- textcoords="offset pixels",
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._text(ra.hours, dec.degrees, labels[i], **text_kwargs)
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._size_multiplier * 1.36),
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, # 4 * self._size_multiplier,
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
- pos = position_of_radec(ra, dec)
124
- self.constellation_id = constellation_at(pos).lower()
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"""
@@ -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.constellation_id = self.iau_id # override from super()
42
+ self._constellation_id = self.iau_id # override from super()
43
43
  self.name = name
44
44
  self.boundary = boundary
45
45