starplot 0.14.0__py2.py3-none-any.whl → 0.15.1__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 (63) hide show
  1. starplot/__init__.py +3 -1
  2. starplot/base.py +149 -37
  3. starplot/cli.py +33 -0
  4. starplot/data/__init__.py +6 -24
  5. starplot/data/bigsky.py +58 -40
  6. starplot/data/constellation_lines.py +827 -0
  7. starplot/data/constellation_stars.py +1501 -0
  8. starplot/data/constellations.py +43 -32
  9. starplot/data/db.py +17 -0
  10. starplot/data/dsos.py +24 -141
  11. starplot/data/library/{stars.bigsky.mag11.parquet → bigsky.0.4.0.stars.mag11.parquet} +0 -0
  12. starplot/data/library/sky.db +0 -0
  13. starplot/data/stars.py +45 -24
  14. starplot/geod.py +0 -6
  15. starplot/geometry.py +105 -6
  16. starplot/horizon.py +118 -107
  17. starplot/map.py +45 -96
  18. starplot/mixins.py +75 -14
  19. starplot/models/__init__.py +1 -1
  20. starplot/models/base.py +10 -128
  21. starplot/models/constellation.py +55 -32
  22. starplot/models/dso.py +132 -67
  23. starplot/models/moon.py +57 -78
  24. starplot/models/planet.py +44 -69
  25. starplot/models/star.py +91 -60
  26. starplot/models/sun.py +32 -53
  27. starplot/optic.py +14 -17
  28. starplot/plotters/constellations.py +81 -78
  29. starplot/plotters/dsos.py +49 -68
  30. starplot/plotters/experimental.py +1 -1
  31. starplot/plotters/milkyway.py +18 -20
  32. starplot/plotters/stars.py +91 -116
  33. starplot/profile.py +16 -0
  34. starplot/settings.py +26 -0
  35. starplot/styles/__init__.py +2 -0
  36. starplot/styles/base.py +7 -17
  37. starplot/styles/ext/blue_gold.yml +135 -0
  38. starplot/styles/ext/blue_light.yml +5 -4
  39. starplot/styles/ext/blue_medium.yml +11 -7
  40. starplot/styles/extensions.py +1 -0
  41. starplot/warnings.py +5 -0
  42. {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/METADATA +11 -11
  43. {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/RECORD +46 -54
  44. starplot-0.15.1.dist-info/entry_points.txt +3 -0
  45. starplot/data/bayer.py +0 -3499
  46. starplot/data/flamsteed.py +0 -2682
  47. starplot/data/library/constellation_borders_inv.gpkg +0 -0
  48. starplot/data/library/constellation_lines_hips.json +0 -709
  49. starplot/data/library/constellation_lines_inv.gpkg +0 -0
  50. starplot/data/library/constellations.gpkg +0 -0
  51. starplot/data/library/constellations_hip.fab +0 -88
  52. starplot/data/library/milkyway.gpkg +0 -0
  53. starplot/data/library/milkyway_inv.gpkg +0 -0
  54. starplot/data/library/ongc.gpkg.zip +0 -0
  55. starplot/data/library/stars.hipparcos.parquet +0 -0
  56. starplot/data/messier.py +0 -111
  57. starplot/data/prep/__init__.py +0 -0
  58. starplot/data/prep/constellations.py +0 -108
  59. starplot/data/prep/dsos.py +0 -299
  60. starplot/data/prep/utils.py +0 -16
  61. starplot/models/geometry.py +0 -44
  62. {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/LICENSE +0 -0
  63. {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/WHEEL +0 -0
starplot/horizon.py CHANGED
@@ -1,3 +1,5 @@
1
+ import math
2
+
1
3
  from datetime import datetime
2
4
  from functools import cache
3
5
 
@@ -8,7 +10,7 @@ from cartopy import crs as ccrs
8
10
  from matplotlib import pyplot as plt, patches
9
11
  from matplotlib.ticker import FixedLocator
10
12
  from skyfield.api import wgs84, Star as SkyfieldStar
11
-
13
+ from shapely import Point
12
14
  from starplot.coordinates import CoordinateSystem
13
15
  from starplot.base import BasePlot, DPI
14
16
  from starplot.mixins import ExtentMaskMixin
@@ -132,25 +134,37 @@ class HorizonPlot(
132
134
  globe=ccrs.Globe(ellipse="sphere", flattening=0),
133
135
  )
134
136
 
135
- self._calc_position()
136
137
  self._init_plot()
137
- self._adjust_radec_minmax()
138
+
139
+ self.altaz_mask = self._extent_mask_altaz()
140
+ self.logger.debug(f"Extent = AZ ({self.az}) ALT ({self.alt})")
141
+
142
+ self._calc_position()
138
143
 
139
144
  @cache
140
145
  def _prepare_coords(self, ra, dec) -> (float, float):
141
146
  """Converts RA/DEC to AZ/ALT"""
142
- point = SkyfieldStar(ra_hours=ra, dec_degrees=dec)
147
+ if ra > 360:
148
+ ra -= 360
149
+ if ra < 0:
150
+ ra += 360
151
+ point = SkyfieldStar(ra_hours=ra / 15, dec_degrees=dec)
143
152
  position = self.observe(point).apparent()
144
153
  pos_alt, pos_az, _ = position.altaz()
145
154
  return pos_az.degrees, pos_alt.degrees
146
155
 
147
- def _prepare_star_coords(self, df):
156
+ def _prepare_star_coords(self, df, limit_by_altaz=True):
148
157
  stars_apparent = self.observe(SkyfieldStar.from_dataframe(df)).apparent()
149
158
  nearby_stars_alt, nearby_stars_az, _ = stars_apparent.altaz()
150
159
  df["x"], df["y"] = (
151
160
  nearby_stars_az.degrees,
152
161
  nearby_stars_alt.degrees,
153
162
  )
163
+ if limit_by_altaz:
164
+ extent = self._extent_mask_altaz()
165
+ df["_geometry_az_alt"] = gpd.points_from_xy(df.x, df.y)
166
+ df = df[df["_geometry_az_alt"].intersects(extent)]
167
+
154
168
  return df
155
169
 
156
170
  def _plot_kwargs(self) -> dict:
@@ -180,108 +194,92 @@ class HorizonPlot(
180
194
  Returns:
181
195
  True if the coordinate is in bounds, otherwise False
182
196
  """
183
- if self.az[0] > 360 or self.az[1] > 360 and az < 90:
184
- az += 360
185
-
186
- return (
187
- az < self.az[1]
188
- and az > self.az[0]
189
- and alt < self.alt[1]
190
- and alt > self.alt[0]
191
- )
197
+ return self.altaz_mask.contains(Point(az, alt))
198
+
199
+ def _in_bounds_xy(self, x: float, y: float) -> bool:
200
+ return self.in_bounds_altaz(y, x) # alt = y, az = x
192
201
 
193
202
  def _polygon(self, points, style, **kwargs):
194
203
  super()._polygon(points, style, transform=self._crs, **kwargs)
195
204
 
196
205
  def _calc_position(self):
197
206
  earth = self.ephemeris["earth"]
198
-
199
207
  self.location = earth + wgs84.latlon(self.lat, self.lon)
200
208
  self.observe = self.location.at(self.timescale).observe
201
209
 
202
- locations = [
203
- self.location.at(self.timescale).from_altaz(
204
- alt_degrees=self.alt[0], az_degrees=self.az[0]
205
- ), # lower left
206
- self.location.at(self.timescale).from_altaz(
207
- alt_degrees=self.alt[0], az_degrees=self.az[1]
208
- ), # lower right
209
- self.location.at(self.timescale).from_altaz(
210
- alt_degrees=self.alt[1], az_degrees=self.center_az
211
- ), # top center
212
- self.location.at(self.timescale).from_altaz(
213
- alt_degrees=self.center_alt, az_degrees=self.center_az
214
- ), # center
215
- # self.location.at(self.timescale).from_altaz(alt_degrees=self.alt[1], az_degrees=self.az[0]), # upper left
216
- # self.location.at(self.timescale).from_altaz(alt_degrees=self.alt[1], az_degrees=self.az[1]), # upper right
217
- ]
218
-
219
- self.ra_min = None
220
- self.ra_max = None
221
- self.dec_max = None
222
- self.dec_min = None
223
-
224
- for location in locations:
225
- ra, dec, _ = location.radec()
226
- ra = ra.hours
227
- dec = dec.degrees
228
- if self.ra_min is None or ra < self.ra_min:
229
- self.ra_min = ra
230
-
231
- if self.ra_max is None or ra > self.ra_max:
232
- self.ra_max = ra
233
-
234
- if self.dec_min is None or dec < self.dec_min:
235
- self.dec_min = dec
236
-
237
- if self.dec_max is None or dec > self.dec_max:
238
- self.dec_max = dec
239
-
240
- def _adjust_radec_minmax(self):
241
- if self.dec_max > 70 or self.dec_min < -70:
242
- # naive method of getting all the stars near the poles
243
- self.ra_min = 0
244
- self.ra_max = 24
210
+ # locations = [
211
+ # self.location.at(self.timescale).from_altaz(
212
+ # alt_degrees=self.alt[0], az_degrees=self.az[0]
213
+ # ), # lower left
214
+ # self.location.at(self.timescale).from_altaz(
215
+ # alt_degrees=self.alt[0], az_degrees=self.az[1]
216
+ # ), # lower right
217
+ # self.location.at(self.timescale).from_altaz(
218
+ # alt_degrees=self.alt[1], az_degrees=self.center_az
219
+ # ), # top center
220
+ # self.location.at(self.timescale).from_altaz(
221
+ # alt_degrees=self.center_alt, az_degrees=self.center_az
222
+ # ), # center
223
+ # self.location.at(self.timescale).from_altaz(alt_degrees=self.alt[1], az_degrees=self.az[0]), # upper left
224
+ # self.location.at(self.timescale).from_altaz(alt_degrees=self.alt[1], az_degrees=self.az[1]), # upper right
225
+ # ]
226
+
227
+ # self.ra_min = None
228
+ # self.ra_max = None
229
+ # self.dec_max = None
230
+ # self.dec_min = None
231
+ # print(self.alt)
232
+ # print(self.az)
233
+ # for location in locations:
234
+ # ra, dec, _ = location.radec()
235
+ # ra = ra.hours
236
+ # dec = dec.degrees
237
+ # print(ra, dec)
238
+ # if self.ra_min is None or ra < self.ra_min:
239
+ # self.ra_min = ra
240
+
241
+ # if self.ra_max is None or ra > self.ra_max:
242
+ # self.ra_max = ra
243
+
244
+ # if self.dec_min is None or dec < self.dec_min:
245
+ # self.dec_min = dec
246
+
247
+ # if self.dec_max is None or dec > self.dec_max:
248
+ # self.dec_max = dec
249
+
250
+ # if self.dec_max > 70 or self.dec_min < -70:
251
+ # # naive method of getting all the stars near the poles
252
+ # self.ra_min = 0
253
+ # self.ra_max = 24
254
+ # else:
255
+ # self.ra_min = max(self.ra_min - 4, 0)
256
+ # self.ra_max = min(self.ra_max + 4, 24)
245
257
 
246
- self.dec_min -= 20
247
- self.dec_max += 20
248
- self.ra_min -= 4
249
- self.ra_max += 4
258
+ # self.dec_min -= 10
259
+ # self.dec_max += 10
250
260
 
251
- if self.ra_min < 0:
252
- self.ra_min = 0
261
+ self.ra_min = 0
262
+ self.ra_max = 360
263
+ self.dec_min = self.lat - 90
264
+ self.dec_max = self.lat + 90
253
265
 
254
266
  self.logger.debug(
255
267
  f"Extent = RA ({self.ra_min:.2f}, {self.ra_max:.2f}) DEC ({self.dec_min:.2f}, {self.dec_max:.2f})"
256
268
  )
257
269
 
258
- def _in_bounds_xy(self, x: float, y: float) -> bool:
259
- return self.in_bounds_altaz(y, x) # alt = y, az = x
270
+ def _adjust_altaz_minmax(self):
271
+ """deprecated"""
272
+ extent = list(self.ax.get_extent(crs=self._plate_carree))
273
+ self.alt = (extent[2], extent[3])
260
274
 
261
- def _read_geo_package(self, filename: str):
262
- """Returns GeoDataFrame of a GeoPackage file"""
275
+ if extent[0] < 0:
276
+ extent[0] += 180
277
+ if extent[1] < 0:
278
+ extent[1] += 180
263
279
 
264
- # if self.ra_min <= 0 and self.ra_max >= 24:
265
- # lon_min = -180
266
- # lon_max = 180
267
- # else:
268
- # lon_min = self.ra_max * 15 - 180 # ra_to_lon(24 - self.ra_max)
269
- # lon_max = self.ra_min * 15 - 180 # ra_to_lon(24 - self.ra_min)
270
-
271
- # extent = self._extent_mask()
272
- # extent = (
273
- # lon_min,
274
- # self.dec_min,
275
- # lon_max,
276
- # self.dec_max,
277
- # )
278
-
279
- return gpd.read_file(
280
- filename,
281
- engine="pyogrio",
282
- use_arrow=True,
283
- # bbox=extent,
284
- )
280
+ self.az = (extent[0], extent[1])
281
+
282
+ self.logger.debug(f"Extent = AZ ({self.az}) ALT ({self.alt})")
285
283
 
286
284
  @use_style(PathStyle, "horizon")
287
285
  def horizon(
@@ -304,12 +302,18 @@ class HorizonPlot(
304
302
  show_ticks: If True, then tick marks will be plotted on the horizon path for every `tick_step` degree that is not also a degree label
305
303
  tick_step: Step size for tick marks
306
304
  """
305
+
306
+ if show_degree_labels or show_ticks:
307
+ patch_y = -0.11 * self.scale
308
+ else:
309
+ patch_y = -0.08 * self.scale
310
+
307
311
  bottom = patches.Polygon(
308
312
  [
309
313
  (0, 0),
310
314
  (1, 0),
311
- (1, -0.1 * self.scale),
312
- (0, -0.1 * self.scale),
315
+ (1, patch_y),
316
+ (0, patch_y),
313
317
  (0, 0),
314
318
  ],
315
319
  color=style.line.color.as_hex(),
@@ -321,47 +325,53 @@ class HorizonPlot(
321
325
  def az_to_ax(d):
322
326
  return self._to_ax(d, self.alt[0])[0]
323
327
 
324
- for az in range(self.az[0] + 2, self.az[1], 1):
328
+ for az in range(int(self.az[0]), int(self.az[1]), 1):
325
329
  az = int(az)
326
330
 
327
331
  if az >= 360:
328
332
  az -= 360
329
333
 
334
+ x = az_to_ax(az)
335
+
336
+ if x <= 0.03 or x >= 0.97 or math.isnan(x):
337
+ continue
338
+
330
339
  if labels.get(az):
331
340
  self.ax.annotate(
332
341
  labels.get(az),
333
- (az_to_ax(az), -0.074 * self.scale),
342
+ (x, patch_y + 0.027),
334
343
  xycoords=self.ax.transAxes,
335
344
  **style.label.matplot_kwargs(self.scale),
336
- clip_on=False,
345
+ clip_on=True,
337
346
  )
338
347
 
339
348
  if show_degree_labels and az % degree_step == 0:
340
349
  self.ax.annotate(
341
350
  str(az) + "\u00b0",
342
- (az_to_ax(az), -0.011 * self.scale),
351
+ (x, -0.011 * self.scale),
343
352
  xycoords=self.ax.transAxes,
344
353
  **self.style.gridlines.label.matplot_kwargs(self.scale),
345
- clip_on=False,
354
+ clip_on=True,
346
355
  )
347
356
 
348
357
  elif show_ticks and az % tick_step == 0:
349
358
  self.ax.annotate(
350
359
  "|",
351
- (az_to_ax(az), -0.011 * self.scale),
360
+ (x, -0.011 * self.scale),
352
361
  xycoords=self.ax.transAxes,
353
362
  **self.style.gridlines.label.matplot_kwargs(self.scale / 2),
354
- clip_on=False,
363
+ clip_on=True,
355
364
  )
356
365
 
357
- self.ax.plot(
358
- [0, 1],
359
- [-0.04 * self.scale, -0.04 * self.scale],
360
- lw=1,
361
- color=style.label.font_color.as_hex(),
362
- clip_on=False,
363
- transform=self.ax.transAxes,
364
- )
366
+ if show_degree_labels or show_ticks:
367
+ self.ax.plot(
368
+ [0, 1],
369
+ [-0.04 * self.scale, -0.04 * self.scale],
370
+ lw=1,
371
+ color=style.label.font_color.as_hex(),
372
+ clip_on=False,
373
+ transform=self.ax.transAxes,
374
+ )
365
375
 
366
376
  @use_style(PathStyle, "gridlines")
367
377
  def gridlines(
@@ -427,6 +437,7 @@ class HorizonPlot(
427
437
  )
428
438
 
429
439
  self.ax.add_patch(self._background_clip_path)
440
+ self._update_clip_path_polygon()
430
441
 
431
442
  def _init_plot(self):
432
443
  self._proj = ccrs.LambertAzimuthalEqualArea(
@@ -454,5 +465,5 @@ class HorizonPlot(
454
465
 
455
466
  self.ax.set_extent(bounds, crs=ccrs.PlateCarree())
456
467
 
457
- self._plot_background_clip_path()
458
468
  self._fit_to_ax()
469
+ self._plot_background_clip_path()
starplot/map.py CHANGED
@@ -8,8 +8,7 @@ from matplotlib import pyplot as plt
8
8
  from matplotlib import path, patches, ticker
9
9
  from matplotlib.ticker import FuncFormatter, FixedLocator
10
10
  from shapely import Polygon
11
- from skyfield.api import Star as SkyfieldStar, wgs84
12
- import geopandas as gpd
11
+ from skyfield.api import wgs84
13
12
  import numpy as np
14
13
 
15
14
  from starplot.coordinates import CoordinateSystem
@@ -36,24 +35,6 @@ from starplot.utils import lon_to_ra, ra_to_lon
36
35
  DEFAULT_MAP_STYLE = PlotStyle() # .extend(extensions.MAP)
37
36
 
38
37
 
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)
53
-
54
- return list(zip(x_coords, y_coords))
55
-
56
-
57
38
  class MapPlot(
58
39
  BasePlot,
59
40
  ExtentMaskMixin,
@@ -69,8 +50,8 @@ class MapPlot(
69
50
 
70
51
  Args:
71
52
  projection: Projection of the map
72
- ra_min: Minimum right ascension of the map's extent, in hours (0...24)
73
- ra_max: Maximum right ascension of the map's extent, in hours (0...24)
53
+ ra_min: Minimum right ascension of the map's extent, in degrees (0...360)
54
+ ra_max: Maximum right ascension of the map's extent, in degrees (0...360)
74
55
  dec_min: Minimum declination of the map's extent, in degrees (-90...90)
75
56
  dec_max: Maximum declination of the map's extent, in degrees (-90...90)
76
57
  lat: Latitude for perspective projections: Orthographic, Stereographic, and Zenith
@@ -96,7 +77,7 @@ class MapPlot(
96
77
  self,
97
78
  projection: Projection,
98
79
  ra_min: float = 0,
99
- ra_max: float = 24,
80
+ ra_max: float = 360,
100
81
  dec_min: float = -90,
101
82
  dec_max: float = 90,
102
83
  lat: float = None,
@@ -154,7 +135,7 @@ class MapPlot(
154
135
 
155
136
  if self.projection == Projection.ZENITH and not self._is_global_extent():
156
137
  raise ValueError(
157
- "Zenith projection requires a global extent: ra_min=0, ra_max=24, dec_min=-90, dec_max=90"
138
+ "Zenith projection requires a global extent: ra_min=0, ra_max=360, dec_min=-90, dec_max=90"
158
139
  )
159
140
 
160
141
  self._geodetic = ccrs.Geodetic()
@@ -172,28 +153,25 @@ class MapPlot(
172
153
  def _plot_kwargs(self) -> dict:
173
154
  return dict(transform=self._crs)
174
155
 
175
- def _prepare_coords(self, ra: float, dec: float) -> (float, float):
176
- return ra * 15, dec
177
-
178
156
  @cache
179
157
  def in_bounds(self, ra: float, dec: float) -> bool:
180
158
  """Determine if a coordinate is within the bounds of the plot.
181
159
 
182
160
  Args:
183
- ra: Right ascension, in hours (0...24)
161
+ ra: Right ascension, in degrees (0...360)
184
162
  dec: Declination, in degrees (-90...90)
185
163
 
186
164
  Returns:
187
165
  True if the coordinate is in bounds, otherwise False
188
166
  """
189
167
  # TODO : try using pyproj transformer directly
190
- x, y = self._proj.transform_point(ra * 15, dec, self._crs)
168
+ x, y = self._proj.transform_point(ra, dec, self._crs)
191
169
  data_to_axes = self.ax.transData + self.ax.transAxes.inverted()
192
170
  x_axes, y_axes = data_to_axes.transform((x, y))
193
171
  return 0 <= x_axes <= 1 and 0 <= y_axes <= 1
194
172
 
195
173
  def _in_bounds_xy(self, x: float, y: float) -> bool:
196
- return self.in_bounds(x / 15, y)
174
+ return self.in_bounds(x, y)
197
175
 
198
176
  def _polygon(self, points, style, **kwargs):
199
177
  super()._polygon(points, style, transform=self._crs, **kwargs)
@@ -201,73 +179,45 @@ class MapPlot(
201
179
  def _latlon_bounds(self):
202
180
  # convert the RA/DEC bounds to lat/lon bounds
203
181
  return [
204
- -1 * self.ra_min * 15,
205
- -1 * self.ra_max * 15,
182
+ -1 * self.ra_min,
183
+ -1 * self.ra_max,
206
184
  self.dec_min,
207
185
  self.dec_max,
208
186
  ]
209
187
 
210
188
  def _adjust_radec_minmax(self):
189
+ # adjust declination to match extent
190
+ extent = self.ax.get_extent(crs=self._plate_carree)
191
+ self.dec_min = extent[2]
192
+ self.dec_max = extent[3]
193
+
211
194
  # adjust the RA min/max if the DEC bounds is near the poles
212
195
  if self.projection in [Projection.STEREO_NORTH, Projection.STEREO_SOUTH] and (
213
196
  self.dec_max > 80 or self.dec_min < -80
214
197
  ):
215
198
  self.ra_min = 0
216
- self.ra_max = 24
217
-
218
- # adjust declination to match extent
219
- extent = self.ax.get_extent(crs=self._plate_carree)
220
- self.dec_min = extent[2]
221
- self.dec_max = extent[3]
199
+ self.ra_max = 360
222
200
 
223
- # adjust right ascension to match extent
224
- if self.ra_max < 24:
225
- ra_min = (-1 * extent[1]) / 15
226
- ra_max = (-1 * extent[0]) / 15
201
+ elif self.ra_max < 360:
202
+ # adjust right ascension to match extent
203
+ ra_min = extent[1] * -1
204
+ ra_max = extent[0] * -1
227
205
 
228
206
  if ra_min < 0 or ra_max < 0:
229
- ra_min += 24
230
- ra_max += 24
207
+ ra_min += 360
208
+ ra_max += 360
231
209
 
232
210
  self.ra_min = ra_min
233
211
  self.ra_max = ra_max
234
212
 
213
+ else:
214
+ self.ra_min = lon_to_ra(extent[1]) * 15
215
+ self.ra_max = lon_to_ra(extent[0]) * 15 + 360
216
+
235
217
  self.logger.debug(
236
218
  f"Extent = RA ({self.ra_min:.2f}, {self.ra_max:.2f}) DEC ({self.dec_min:.2f}, {self.dec_max:.2f})"
237
219
  )
238
220
 
239
- def _read_geo_package(self, filename: str):
240
- """Returns GeoDataFrame of a GeoPackage file"""
241
- extent = self.ax.get_extent(crs=self._plate_carree)
242
- bbox = (extent[0], extent[2], extent[1], extent[3])
243
-
244
- return gpd.read_file(
245
- filename,
246
- engine="pyogrio",
247
- use_arrow=True,
248
- bbox=bbox,
249
- )
250
-
251
- def _load_stars(self, catalog, limiting_magnitude):
252
- df = super()._load_stars(catalog, limiting_magnitude)
253
-
254
- if self.projection == Projection.ZENITH:
255
- # filter stars for zenith plots to only include those above horizon
256
- earth = self.ephemeris["earth"]
257
- self.location = earth + wgs84.latlon(self.lat, self.lon)
258
-
259
- stars_apparent = (
260
- self.location.at(self.timescale)
261
- .observe(SkyfieldStar.from_dataframe(df))
262
- .apparent()
263
- )
264
- # we only need altitude
265
- stars_alt, _, _ = stars_apparent.altaz()
266
- df["alt"] = stars_alt.degrees
267
- df = df[df["alt"] > 0]
268
-
269
- return df
270
-
271
221
  @use_style(ObjectStyle, "zenith")
272
222
  def zenith(
273
223
  self,
@@ -292,7 +242,7 @@ class MapPlot(
292
242
  ra, dec, _ = zenith.radec()
293
243
 
294
244
  self.marker(
295
- ra=ra.hours,
245
+ ra=ra.hours * 15,
296
246
  dec=dec.degrees,
297
247
  style=style,
298
248
  label=label,
@@ -321,7 +271,7 @@ class MapPlot(
321
271
  ra, dec, _ = zenith.radec()
322
272
 
323
273
  points = geod.ellipse(
324
- center=(ra.hours, dec.degrees),
274
+ center=(ra.hours * 15, dec.degrees),
325
275
  height_degrees=180,
326
276
  width_degrees=180,
327
277
  num_pts=100,
@@ -330,7 +280,6 @@ class MapPlot(
330
280
  y = []
331
281
 
332
282
  for ra, dec in points:
333
- ra = ra / 15
334
283
  x0, y0 = self._prepare_coords(ra, dec)
335
284
  x.append(x0)
336
285
  y.append(y0)
@@ -353,6 +302,9 @@ class MapPlot(
353
302
  )
354
303
  self.ax.add_patch(patch)
355
304
  self._background_clip_path = patch
305
+ self._update_clip_path_polygon(
306
+ buffer=style.line.width / 2 + 2 * style.line.edge_width + 20
307
+ )
356
308
 
357
309
  if not labels:
358
310
  return
@@ -398,21 +350,18 @@ class MapPlot(
398
350
 
399
351
  text_kwargs = dict(
400
352
  **style.label.matplot_kwargs(self.scale),
401
- hide_on_collision=False,
402
353
  xytext=(
403
354
  style.label.offset_x * self.scale,
404
355
  style.label.offset_y * self.scale,
405
356
  ),
406
357
  textcoords="offset points",
407
358
  path_effects=[],
359
+ clip_on=True,
408
360
  )
409
361
 
410
- if self.projection == Projection.ZENITH:
411
- text_kwargs["clip_on"] = False
412
-
413
362
  for i, position in enumerate(cardinal_directions):
414
363
  ra, dec, _ = position.radec()
415
- x, y = self._prepare_coords(ra, dec)
364
+ x, y = self._prepare_coords(ra.hours * 15, dec.degrees)
416
365
  self._text(x, y, labels[i], **text_kwargs)
417
366
 
418
367
  @use_style(PathStyle, "gridlines")
@@ -433,12 +382,12 @@ class MapPlot(
433
382
  Args:
434
383
  style: Styling of the gridlines. If None, then the plot's style (specified when creating the plot) will be used
435
384
  labels: If True, then labels for each gridline will be plotted on the outside of the axes.
436
- ra_locations: List of Right Ascension locations for the gridlines (in hours, 0...24). Defaults to every 1 hour.
385
+ ra_locations: List of Right Ascension locations for the gridlines (in degrees, 0...360). Defaults to every 15 degrees.
437
386
  dec_locations: List of Declination locations for the gridlines (in degrees, -90...90). Defaults to every 10 degrees.
438
387
  ra_formatter_fn: Callable for creating labels of right ascension gridlines
439
388
  dec_formatter_fn: Callable for creating labels of declination gridlines
440
389
  tick_marks: If True, then tick marks will be plotted outside the axis. **Only supported for rectangular projections (e.g. Mercator, Miller)**
441
- ra_tick_locations: List of Right Ascension locations for the tick marks (in hours, 0...24)
390
+ ra_tick_locations: List of Right Ascension locations for the tick marks (in degrees, 0...260)
442
391
  dec_tick_locations: List of Declination locations for the tick marks (in degrees, -90...90)
443
392
  """
444
393
 
@@ -455,7 +404,7 @@ class MapPlot(
455
404
  def dec_formatter(x, pos) -> str:
456
405
  return dec_formatter_fn(x)
457
406
 
458
- ra_locations = ra_locations or [x for x in range(24)]
407
+ ra_locations = ra_locations or [x for x in range(0, 360, 15)]
459
408
  dec_locations = dec_locations or [d for d in range(-80, 90, 10)]
460
409
 
461
410
  line_style_kwargs = style.line.matplot_kwargs()
@@ -484,14 +433,14 @@ class MapPlot(
484
433
  # because cartopy does not extend lines to poles
485
434
  for ra in ra_locations:
486
435
  self.ax.plot(
487
- (ra * 15, ra * 15),
436
+ (ra, ra),
488
437
  (-90, 90),
489
438
  gid="gridlines",
490
439
  **line_style_kwargs,
491
440
  **self._plot_kwargs(),
492
441
  )
493
442
 
494
- gridlines.xlocator = FixedLocator([ra_to_lon(r) for r in ra_locations])
443
+ gridlines.xlocator = FixedLocator([ra_to_lon(r / 15) for r in ra_locations])
495
444
  gridlines.xformatter = FuncFormatter(ra_formatter)
496
445
  gridlines.xlabel_style = label_style_kwargs
497
446
 
@@ -506,10 +455,10 @@ class MapPlot(
506
455
  def in_axes(ra):
507
456
  return self.in_bounds(ra, (self.dec_max + self.dec_min) / 2)
508
457
 
509
- xticks = ra_tick_locations or [x for x in np.arange(0, 24, 0.125)]
458
+ xticks = ra_tick_locations or [x for x in np.arange(0, 360, 1.875)]
510
459
  yticks = dec_tick_locations or [x for x in np.arange(-90, 90, 1)]
511
460
 
512
- inbound_xticks = [ra_to_lon(ra) for ra in xticks if in_axes(ra)]
461
+ inbound_xticks = [ra_to_lon(ra / 15) for ra in xticks if in_axes(ra)]
513
462
  self.ax.set_xticks(inbound_xticks, crs=self._plate_carree)
514
463
  self.ax.xaxis.set_major_formatter(ticker.NullFormatter())
515
464
 
@@ -551,7 +500,7 @@ class MapPlot(
551
500
  Projection.STEREOGRAPHIC,
552
501
  Projection.ZENITH,
553
502
  ]:
554
- # Calculate LST to shift RA DEC to be in line with current date and time
503
+ # Calculate local sidereal time (LST) to shift RA DEC to be in line with current date and time
555
504
  lst = -(360.0 * self.timescale.gmst / 24.0 + self.lon) % 360.0
556
505
  self._proj = Projection.crs(self.projection, lon=lst, lat=self.lat)
557
506
  elif self.projection == Projection.LAMBERT_AZ_EQ_AREA:
@@ -583,9 +532,8 @@ class MapPlot(
583
532
 
584
533
  self.logger.debug(f"Projection = {self.projection.value.upper()}")
585
534
 
586
- self._plot_background_clip_path()
587
-
588
535
  self._fit_to_ax()
536
+ self._plot_background_clip_path()
589
537
 
590
538
  @use_style(LabelStyle, "info_text")
591
539
  def info(self, style: LabelStyle = None):
@@ -614,14 +562,14 @@ class MapPlot(
614
562
  trans = self.ax.transAxes + self.ax.transData.inverted()
615
563
  x_projected, y_projected = trans.transform((x, y)) # axes to data
616
564
  x_ra, y_ra = self._crs.transform_point(x_projected, y_projected, self._proj)
617
- return (x_ra + 360) / 15, y_ra
565
+ return (x_ra + 360), y_ra
618
566
 
619
567
  def _plot_background_clip_path(self):
620
568
  def to_axes(points):
621
569
  ax_points = []
622
570
 
623
571
  for ra, dec in points:
624
- x, y = self._proj.transform_point(ra * 15, dec, self._crs)
572
+ x, y = self._proj.transform_point(ra, dec, self._crs)
625
573
  data_to_axes = self.ax.transData + self.ax.transAxes.inverted()
626
574
  x_axes, y_axes = data_to_axes.transform((x, y))
627
575
  ax_points.append([x_axes, y_axes])
@@ -662,3 +610,4 @@ class MapPlot(
662
610
  )
663
611
 
664
612
  self.ax.add_patch(self._background_clip_path)
613
+ self._update_clip_path_polygon()