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.
- starplot/__init__.py +3 -1
- starplot/base.py +149 -37
- starplot/cli.py +33 -0
- starplot/data/__init__.py +6 -24
- starplot/data/bigsky.py +58 -40
- starplot/data/constellation_lines.py +827 -0
- starplot/data/constellation_stars.py +1501 -0
- starplot/data/constellations.py +43 -32
- starplot/data/db.py +17 -0
- starplot/data/dsos.py +24 -141
- starplot/data/library/{stars.bigsky.mag11.parquet → bigsky.0.4.0.stars.mag11.parquet} +0 -0
- starplot/data/library/sky.db +0 -0
- starplot/data/stars.py +45 -24
- starplot/geod.py +0 -6
- starplot/geometry.py +105 -6
- starplot/horizon.py +118 -107
- starplot/map.py +45 -96
- starplot/mixins.py +75 -14
- starplot/models/__init__.py +1 -1
- starplot/models/base.py +10 -128
- starplot/models/constellation.py +55 -32
- starplot/models/dso.py +132 -67
- starplot/models/moon.py +57 -78
- starplot/models/planet.py +44 -69
- starplot/models/star.py +91 -60
- starplot/models/sun.py +32 -53
- starplot/optic.py +14 -17
- starplot/plotters/constellations.py +81 -78
- starplot/plotters/dsos.py +49 -68
- starplot/plotters/experimental.py +1 -1
- starplot/plotters/milkyway.py +18 -20
- starplot/plotters/stars.py +91 -116
- starplot/profile.py +16 -0
- starplot/settings.py +26 -0
- starplot/styles/__init__.py +2 -0
- starplot/styles/base.py +7 -17
- starplot/styles/ext/blue_gold.yml +135 -0
- starplot/styles/ext/blue_light.yml +5 -4
- starplot/styles/ext/blue_medium.yml +11 -7
- starplot/styles/extensions.py +1 -0
- starplot/warnings.py +5 -0
- {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/METADATA +11 -11
- {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/RECORD +46 -54
- starplot-0.15.1.dist-info/entry_points.txt +3 -0
- starplot/data/bayer.py +0 -3499
- starplot/data/flamsteed.py +0 -2682
- starplot/data/library/constellation_borders_inv.gpkg +0 -0
- starplot/data/library/constellation_lines_hips.json +0 -709
- starplot/data/library/constellation_lines_inv.gpkg +0 -0
- starplot/data/library/constellations.gpkg +0 -0
- starplot/data/library/constellations_hip.fab +0 -88
- starplot/data/library/milkyway.gpkg +0 -0
- starplot/data/library/milkyway_inv.gpkg +0 -0
- starplot/data/library/ongc.gpkg.zip +0 -0
- starplot/data/library/stars.hipparcos.parquet +0 -0
- starplot/data/messier.py +0 -111
- starplot/data/prep/__init__.py +0 -0
- starplot/data/prep/constellations.py +0 -108
- starplot/data/prep/dsos.py +0 -299
- starplot/data/prep/utils.py +0 -16
- starplot/models/geometry.py +0 -44
- {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/LICENSE +0 -0
- {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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
]
|
|
218
|
-
|
|
219
|
-
self.ra_min = None
|
|
220
|
-
self.ra_max = None
|
|
221
|
-
self.dec_max = None
|
|
222
|
-
self.dec_min = None
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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 -=
|
|
247
|
-
self.dec_max +=
|
|
248
|
-
self.ra_min -= 4
|
|
249
|
-
self.ra_max += 4
|
|
258
|
+
# self.dec_min -= 10
|
|
259
|
+
# self.dec_max += 10
|
|
250
260
|
|
|
251
|
-
|
|
252
|
-
|
|
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
|
|
259
|
-
|
|
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
|
-
|
|
262
|
-
|
|
275
|
+
if extent[0] < 0:
|
|
276
|
+
extent[0] += 180
|
|
277
|
+
if extent[1] < 0:
|
|
278
|
+
extent[1] += 180
|
|
263
279
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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,
|
|
312
|
-
(0,
|
|
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]
|
|
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
|
-
(
|
|
342
|
+
(x, patch_y + 0.027),
|
|
334
343
|
xycoords=self.ax.transAxes,
|
|
335
344
|
**style.label.matplot_kwargs(self.scale),
|
|
336
|
-
clip_on=
|
|
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
|
-
(
|
|
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=
|
|
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
|
-
(
|
|
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=
|
|
363
|
+
clip_on=True,
|
|
355
364
|
)
|
|
356
365
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
|
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
|
|
73
|
-
ra_max: Maximum right ascension of the map's extent, in
|
|
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 =
|
|
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=
|
|
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
|
|
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
|
|
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
|
|
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
|
|
205
|
-
-1 * self.ra_max
|
|
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 =
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
ra_min =
|
|
226
|
-
ra_max =
|
|
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 +=
|
|
230
|
-
ra_max +=
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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,
|
|
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)
|
|
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
|
|
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()
|