starplot 0.15.6__py2.py3-none-any.whl → 0.15.8__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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """Star charts and maps of the sky"""
2
2
 
3
- __version__ = "0.15.6"
3
+ __version__ = "0.15.8"
4
4
 
5
5
  from .base import BasePlot # noqa: F401
6
6
  from .map import MapPlot, Projection # noqa: F401
@@ -1,3 +1,5 @@
1
+ from functools import cache
2
+
1
3
  import ibis
2
4
  from ibis import _
3
5
 
@@ -669,23 +671,35 @@ CONSTELLATION_HIP_IDS = {
669
671
  }
670
672
 
671
673
 
672
- def load(extent=None, filters=None):
673
- filters = filters or []
674
+ @cache
675
+ def table():
674
676
  con = db.connect()
675
677
  c = con.table("constellations")
676
- c = c.mutate(
678
+
679
+ return c.mutate(
677
680
  ra=_.center_ra,
678
681
  dec=_.center_dec,
679
682
  constellation_id=_.iau_id,
680
- rowid=ibis.row_number(),
681
683
  boundary=_.geometry,
684
+ rowid=ibis.row_number(),
685
+ sk=ibis.row_number(),
682
686
  )
683
687
 
688
+
689
+ def load(extent=None, filters=None, sql=None):
690
+ filters = filters or []
691
+ c = table()
692
+
684
693
  if extent:
685
694
  filters.append(_.geometry.intersects(extent))
686
695
 
687
696
  if filters:
688
- return c.filter(*filters)
697
+ c = c.filter(*filters)
698
+
699
+ if sql:
700
+ result = c.alias("_").sql(sql).select("sk").execute()
701
+ skids = result["sk"].to_list()
702
+ c = c.filter(_.sk.isin(skids))
689
703
 
690
704
  return c
691
705
 
starplot/data/dsos.py CHANGED
@@ -1,3 +1,5 @@
1
+ from functools import cache
2
+
1
3
  import ibis
2
4
  from ibis import _
3
5
 
@@ -25,26 +27,37 @@ class DsoLabelMaker(dict):
25
27
  DSO_LABELS_DEFAULT = DsoLabelMaker()
26
28
 
27
29
 
28
- def load(extent=None, filters=None):
29
- filters = filters or []
30
+ @cache
31
+ def table():
30
32
  con = db.connect()
31
33
  dsos = con.table("deep_sky_objects")
32
34
 
33
- dsos = dsos.mutate(
35
+ return dsos.mutate(
34
36
  ra=_.ra_degrees,
35
37
  dec=_.dec_degrees,
36
38
  constellation_id=_.constellation,
37
39
  magnitude=ibis.coalesce(_.mag_v, _.mag_b, None),
38
40
  size=_.size_deg2,
39
41
  rowid=ibis.row_number(),
42
+ sk=ibis.row_number(),
40
43
  )
41
44
 
45
+
46
+ def load(extent=None, filters=None, sql=None):
47
+ filters = filters or []
48
+ dsos = table()
49
+
42
50
  if extent:
43
51
  dsos = dsos.filter(_.geometry.intersects(extent))
44
52
 
45
53
  filters.extend([_.ra_degrees.notnull() & _.dec_degrees.notnull()])
46
54
 
47
55
  if filters:
48
- return dsos.filter(*filters)
56
+ dsos = dsos.filter(*filters)
57
+
58
+ if sql:
59
+ result = dsos.alias("_").sql(sql).select("sk").execute()
60
+ skids = result["sk"].to_list()
61
+ dsos = dsos.filter(_.sk.isin(skids))
49
62
 
50
63
  return dsos
Binary file
starplot/data/stars.py CHANGED
@@ -459,7 +459,7 @@ class StarCatalog:
459
459
 
460
460
 
461
461
  @cache
462
- def read_catalog(catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11, table_name="stars"):
462
+ def table(catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11, table_name="stars"):
463
463
  con = db.connect()
464
464
 
465
465
  if catalog == StarCatalog.BIG_SKY_MAG11:
@@ -481,6 +481,7 @@ def read_catalog(catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11, table_name="s
481
481
  # stars parquet does not have geometry field
482
482
  geometry=_.ra_degrees.point(_.dec_degrees),
483
483
  rowid=ibis.row_number(),
484
+ sk=ibis.row_number(),
484
485
  )
485
486
 
486
487
  stars = stars.join(
@@ -495,14 +496,24 @@ def read_catalog(catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11, table_name="s
495
496
  return stars
496
497
 
497
498
 
498
- def load(extent=None, catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11, filters=None):
499
+ def load(
500
+ extent=None,
501
+ catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11,
502
+ filters=None,
503
+ sql=None,
504
+ ):
499
505
  filters = filters or []
500
- stars = read_catalog(catalog)
506
+ stars = table(catalog)
501
507
 
502
508
  if extent:
503
509
  stars = stars.filter(stars.geometry.intersects(extent))
504
510
 
505
511
  if filters:
506
- return stars.filter(*filters)
512
+ stars = stars.filter(*filters)
513
+
514
+ if sql:
515
+ result = stars.alias("_").sql(sql).select("sk").execute()
516
+ skids = result["sk"].to_list()
517
+ stars = stars.filter(_.sk.isin(skids))
507
518
 
508
519
  return stars
starplot/models/base.py CHANGED
@@ -1,40 +1,86 @@
1
+ import json
2
+
1
3
  from functools import cache
2
4
  from typing import Optional
3
5
 
6
+ from shapely import to_geojson, from_geojson, Geometry
7
+ from shapely.geometry import Polygon, MultiPolygon, Point
8
+
9
+ from pydantic_core import core_schema
10
+
11
+ from pydantic import (
12
+ BaseModel,
13
+ computed_field,
14
+ )
4
15
  from skyfield.api import position_of_radec, load_constellation_map
5
16
 
6
17
  from starplot.mixins import CreateMapMixin, CreateOpticMixin
7
18
 
8
19
 
20
+ class ShapelyPydantic:
21
+ @classmethod
22
+ def validate(cls, field_value, info):
23
+ return field_value.is_valid
24
+
25
+ @classmethod
26
+ def __get_pydantic_core_schema__(cls, source, handler) -> core_schema.CoreSchema:
27
+ def validate_from_geojson(value: dict) -> Geometry:
28
+ return from_geojson(json.loads(value))
29
+
30
+ from_dict_schema = core_schema.chain_schema(
31
+ [
32
+ core_schema.dict_schema(),
33
+ core_schema.no_info_plain_validator_function(validate_from_geojson),
34
+ ]
35
+ )
36
+
37
+ return core_schema.json_or_python_schema(
38
+ json_schema=from_dict_schema,
39
+ python_schema=core_schema.union_schema(
40
+ [
41
+ core_schema.is_instance_schema(Geometry),
42
+ from_dict_schema,
43
+ ]
44
+ ),
45
+ serialization=core_schema.plain_serializer_function_ser_schema(
46
+ lambda instance: json.loads(to_geojson(instance))
47
+ ),
48
+ )
49
+
50
+
51
+ class ShapelyPolygon(ShapelyPydantic, Polygon):
52
+ pass
53
+
54
+
55
+ class ShapelyMultiPolygon(ShapelyPydantic, MultiPolygon):
56
+ pass
57
+
58
+
59
+ class ShapelyPoint(ShapelyPydantic, Point):
60
+ pass
61
+
62
+
9
63
  @cache
10
64
  def constellation_at():
11
65
  return load_constellation_map()
12
66
 
13
67
 
14
- class SkyObject(CreateMapMixin, CreateOpticMixin):
68
+ class SkyObject(BaseModel, CreateMapMixin, CreateOpticMixin):
15
69
  """
16
70
  Basic sky object model.
17
71
  """
18
72
 
19
73
  ra: float
20
- """Right Ascension, in degrees (0...360)"""
74
+ """Right Ascension, in degrees (0 to 360)"""
21
75
 
22
76
  dec: float
23
- """Declination, in degrees (-90...90)"""
24
-
25
- _constellation_id = None
26
-
27
- constellation_id: Optional[str] = None
28
- """Identifier of the constellation that contains this object. The ID is the three-letter (all lowercase) abbreviation from the International Astronomical Union (IAU)."""
77
+ """Declination, in degrees (-90 to 90)"""
29
78
 
30
- def __init__(self, ra: float, dec: float, constellation_id: str = None) -> None:
31
- self.ra = ra
32
- self.dec = dec
33
- if constellation_id:
34
- self._constellation_id = constellation_id
79
+ _constellation_id: Optional[str] = None
35
80
 
81
+ @computed_field
36
82
  @property
37
- def constellation_id(self):
83
+ def constellation_id(self) -> str | None:
38
84
  """Identifier of the constellation that contains this object. The ID is the three-letter (all lowercase) abbreviation from the International Astronomical Union (IAU)."""
39
85
  if not self._constellation_id:
40
86
  pos = position_of_radec(self.ra, self.dec)
@@ -1,9 +1,8 @@
1
- from typing import Union, Iterator
1
+ from typing import Iterator
2
2
 
3
3
  from ibis import _
4
- from shapely import Polygon, MultiPolygon
5
4
 
6
- from starplot.models.base import SkyObject
5
+ from starplot.models.base import SkyObject, ShapelyPolygon, ShapelyMultiPolygon
7
6
  from starplot.data import constellations
8
7
 
9
8
 
@@ -21,29 +20,14 @@ class Constellation(SkyObject):
21
20
  """
22
21
 
23
22
  name: str = None
24
- """Name"""
23
+ """Name of constellation"""
25
24
 
26
25
  star_hip_ids: list[int] = None
27
26
  """List of HIP ids for stars that are part of the _lines_ for this constellation."""
28
27
 
29
- boundary: Union[Polygon, MultiPolygon] = None
28
+ boundary: ShapelyPolygon | ShapelyMultiPolygon = None
30
29
  """Shapely Polygon of the constellation's boundary. Right ascension coordinates are in degrees (0...360)."""
31
30
 
32
- def __init__(
33
- self,
34
- ra: float,
35
- dec: float,
36
- iau_id: str,
37
- name: str = None,
38
- star_hip_ids: list[int] = None,
39
- boundary: Polygon = None,
40
- ) -> None:
41
- super().__init__(ra, dec, constellation_id=iau_id.lower())
42
- self.iau_id = iau_id.lower()
43
- self.name = name
44
- self.star_hip_ids = star_hip_ids
45
- self.boundary = boundary
46
-
47
31
  def __repr__(self) -> str:
48
32
  return f"Constellation(iau_id={self.iau_id}, name={self.name}, ra={self.ra}, dec={self.dec})"
49
33
 
@@ -55,7 +39,7 @@ class Constellation(SkyObject):
55
39
  yield from_tuple(c)
56
40
 
57
41
  @classmethod
58
- def get(cls, **kwargs) -> "Constellation":
42
+ def get(cls, sql: str = None, **kwargs) -> "Constellation":
59
43
  """
60
44
  Get a Constellation, by matching its attributes.
61
45
 
@@ -64,6 +48,7 @@ class Constellation(SkyObject):
64
48
  hercules = Constellation.get(name="Hercules")
65
49
 
66
50
  Args:
51
+ sql: SQL query for selecting constellation (table name is "_")
67
52
  **kwargs: Attributes on the constellation you want to match
68
53
 
69
54
  Raises: `ValueError` if more than one constellation is matched
@@ -73,7 +58,7 @@ class Constellation(SkyObject):
73
58
  for k, v in kwargs.items():
74
59
  filters.append(getattr(_, k) == v)
75
60
 
76
- df = constellations.load(filters=filters).to_pandas()
61
+ df = constellations.load(filters=filters, sql=sql).to_pandas()
77
62
  results = [from_tuple(c) for c in df.itertuples()]
78
63
 
79
64
  if len(results) == 1:
@@ -87,18 +72,19 @@ class Constellation(SkyObject):
87
72
  return None
88
73
 
89
74
  @classmethod
90
- def find(cls, where: list) -> list["Constellation"]:
75
+ def find(cls, where: list = None, sql: str = None) -> list["Constellation"]:
91
76
  """
92
77
  Find Constellations
93
78
 
94
79
  Args:
95
80
  where: A list of expressions that determine which constellations to find. See [Selecting Objects](/reference-selecting-objects/) for details.
81
+ sql: SQL query for selecting constellations (table name is "_")
96
82
 
97
83
  Returns:
98
84
  List of Constellations that match all `where` expressions
99
85
 
100
86
  """
101
- df = constellations.load(filters=where).to_pandas()
87
+ df = constellations.load(filters=where, sql=sql).to_pandas()
102
88
 
103
89
  return [from_tuple(c) for c in df.itertuples()]
104
90
 
@@ -108,11 +94,13 @@ class Constellation(SkyObject):
108
94
 
109
95
 
110
96
  def from_tuple(c: tuple) -> Constellation:
111
- return Constellation(
97
+ c = Constellation(
112
98
  ra=c.ra,
113
99
  dec=c.dec,
114
- iau_id=c.iau_id,
100
+ iau_id=c.iau_id.lower(),
115
101
  name=c.name,
116
102
  star_hip_ids=c.star_hip_ids,
117
103
  boundary=c.geometry,
118
104
  )
105
+ c._constellation_id = c.iau_id
106
+ return c
starplot/models/dso.py CHANGED
@@ -1,39 +1,80 @@
1
- from typing import Optional, Union, Iterator
1
+ from typing import Optional, Iterator
2
+ from enum import Enum
2
3
 
3
4
  from ibis import _
4
- from shapely.geometry import Polygon, MultiPolygon
5
5
 
6
6
  from starplot.data.dsos import load
7
7
  from starplot.mixins import CreateMapMixin, CreateOpticMixin
8
- from starplot.models.base import SkyObject
8
+ from starplot.models.base import SkyObject, ShapelyPolygon, ShapelyMultiPolygon
9
9
 
10
10
 
11
- class DsoType:
11
+ class DsoType(str, Enum):
12
12
  """
13
- Type of deep sky object (DSOs), as designated in OpenNGC
13
+ Type of deep sky object (DSO), as designated in OpenNGC
14
14
  """
15
15
 
16
16
  STAR = "*"
17
+ """Star"""
18
+
17
19
  DOUBLE_STAR = "**"
20
+ """Double star or multiple star system"""
21
+
18
22
  ASSOCIATION_OF_STARS = "*Ass"
23
+ """Association of stars"""
24
+
19
25
  OPEN_CLUSTER = "OCl"
26
+ """Open cluster of stars"""
27
+
20
28
  GLOBULAR_CLUSTER = "GCl"
29
+ """Globular cluster of stars"""
30
+
21
31
  GALAXY = "G"
32
+ """Galaxy"""
33
+
22
34
  GALAXY_PAIR = "GPair"
35
+ """Group of two galaxies"""
36
+
23
37
  GALAXY_TRIPLET = "GTrpl"
38
+ """Group of three galaxies"""
39
+
24
40
  GROUP_OF_GALAXIES = "GGroup"
41
+ """Group of more than three galaxies"""
42
+
25
43
  NEBULA = "Neb"
44
+ """Nebula"""
45
+
26
46
  PLANETARY_NEBULA = "PN"
47
+ """Planetary nebula"""
48
+
27
49
  EMISSION_NEBULA = "EmN"
50
+ """Emission Nebula"""
51
+
28
52
  STAR_CLUSTER_NEBULA = "Cl+N"
53
+ """Star cluster with nebulosity"""
54
+
29
55
  REFLECTION_NEBULA = "RfN"
56
+ """Reflection nebula"""
57
+
30
58
  DARK_NEBULA = "DrkN"
59
+ """Dark nebula"""
60
+
31
61
  HII_IONIZED_REGION = "HII"
62
+ """Hydrogen ionized region"""
63
+
32
64
  SUPERNOVA_REMNANT = "SNR"
65
+ """Supernova remnant"""
66
+
33
67
  NOVA_STAR = "Nova"
68
+ """Nova star"""
69
+
34
70
  NONEXISTENT = "NonEx"
71
+ """Non-existant object"""
72
+
35
73
  UNKNOWN = "Other"
74
+ """Unknown type of object"""
75
+
36
76
  DUPLICATE_RECORD = "Dup"
77
+ """Duplicate record of another object"""
37
78
 
38
79
 
39
80
  class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
@@ -78,39 +119,9 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
78
119
  Index Catalogue (IC) identifier. *Note that this field is a string, to support objects like '4974 NED01'.*
79
120
  """
80
121
 
81
- geometry: Union[Polygon, MultiPolygon] = None
122
+ geometry: ShapelyPolygon | ShapelyMultiPolygon = None
82
123
  """Shapely Polygon of the DSO's extent. Right ascension coordinates are in degrees (0...360)."""
83
124
 
84
- def __init__(
85
- self,
86
- ra: float,
87
- dec: float,
88
- name: str,
89
- type: DsoType,
90
- magnitude: float = None,
91
- maj_ax: float = None,
92
- min_ax: float = None,
93
- angle: float = None,
94
- size: float = None,
95
- m: str = None,
96
- ngc: str = None,
97
- ic: str = None,
98
- geometry: Union[Polygon, MultiPolygon] = None,
99
- constellation_id: str = None,
100
- ) -> None:
101
- super().__init__(ra, dec, constellation_id)
102
- self.name = name
103
- self.type = type
104
- self.magnitude = magnitude
105
- self.maj_ax = maj_ax
106
- self.min_ax = min_ax
107
- self.angle = angle
108
- self.size = size
109
- self.m = m
110
- self.ngc = ngc
111
- self.ic = ic
112
- self.geometry = geometry
113
-
114
125
  def __repr__(self) -> str:
115
126
  return f"DSO(name={self.name}, magnitude={self.magnitude})"
116
127
 
@@ -122,7 +133,7 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
122
133
  yield from_tuple(d)
123
134
 
124
135
  @classmethod
125
- def get(cls, **kwargs) -> "DSO":
136
+ def get(cls, sql: str = None, **kwargs) -> "DSO":
126
137
  """
127
138
  Get a DSO, by matching its attributes.
128
139
 
@@ -131,6 +142,7 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
131
142
  d = DSO.get(m=13)
132
143
 
133
144
  Args:
145
+ sql: SQL query for selecting DSO (table name is "_")
134
146
  **kwargs: Attributes on the DSO you want to match
135
147
 
136
148
  Raises: `ValueError` if more than one DSO is matched
@@ -140,7 +152,7 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
140
152
  for k, v in kwargs.items():
141
153
  filters.append(getattr(_, k) == v)
142
154
 
143
- df = load(filters=filters).to_pandas()
155
+ df = load(filters=filters, sql=sql).to_pandas()
144
156
 
145
157
  results = [from_tuple(d) for d in df.itertuples()]
146
158
 
@@ -155,18 +167,19 @@ class DSO(SkyObject, CreateMapMixin, CreateOpticMixin):
155
167
  return None
156
168
 
157
169
  @classmethod
158
- def find(cls, where: list) -> list["DSO"]:
170
+ def find(cls, where: list = None, sql: str = None) -> list["DSO"]:
159
171
  """
160
172
  Find DSOs
161
173
 
162
174
  Args:
163
175
  where: A list of expressions that determine which DSOs to find. See [Selecting Objects](/reference-selecting-objects/) for details.
176
+ sql: SQL query for selecting DSOs (table name is "_")
164
177
 
165
178
  Returns:
166
179
  List of DSOs that match all `where` expressions
167
180
 
168
181
  """
169
- df = load(filters=where).to_pandas()
182
+ df = load(filters=where, sql=sql).to_pandas()
170
183
  return [from_tuple(d) for d in df.itertuples()]
171
184
 
172
185
 
@@ -185,8 +198,8 @@ def from_tuple(d: tuple) -> DSO:
185
198
  ngc=d.ngc,
186
199
  ic=d.ic,
187
200
  geometry=d.geometry,
188
- constellation_id=d.constellation_id,
189
201
  )
202
+ dso._constellation_id = d.constellation_id
190
203
  dso._row_id = getattr(d, "rowid", None)
191
204
  return dso
192
205
 
starplot/models/moon.py CHANGED
@@ -4,10 +4,9 @@ from enum import Enum
4
4
  import numpy as np
5
5
  from skyfield.api import Angle, wgs84
6
6
  from skyfield import almanac
7
- from shapely import Polygon
8
7
 
9
8
  from starplot.data import load
10
- from starplot.models.base import SkyObject
9
+ from starplot.models.base import SkyObject, ShapelyPolygon
11
10
  from starplot.geometry import circle
12
11
  from starplot.utils import dt_or_now
13
12
 
@@ -35,7 +34,7 @@ class Moon(SkyObject):
35
34
  """Date/time of moon's position"""
36
35
 
37
36
  apparent_size: float
38
- """Apparent size (degrees)"""
37
+ """Apparent size in the sky (degrees)"""
39
38
 
40
39
  phase_angle: float
41
40
  """Angle of the moon from the Sun (degrees)"""
@@ -44,31 +43,10 @@ class Moon(SkyObject):
44
43
  """Description of the moon's phase. The Moon will be considered New/Full/Quarter if it's within 12 hours of that precise phase."""
45
44
 
46
45
  illumination: float
47
- """Percent of illumination (0...1)"""
48
-
49
- geometry: Polygon = None
50
- """Shapely Polygon of the moon's extent. Right ascension coordinates are in degrees (0...360)."""
51
-
52
- def __init__(
53
- self,
54
- ra: float,
55
- dec: float,
56
- name: str,
57
- dt: datetime,
58
- apparent_size: float,
59
- phase_angle: float,
60
- phase_description: str,
61
- illumination: str,
62
- geometry: Polygon = None,
63
- ) -> None:
64
- super().__init__(ra, dec)
65
- self.name = name
66
- self.dt = dt
67
- self.apparent_size = apparent_size
68
- self.phase_angle = phase_angle
69
- self.phase_description = phase_description
70
- self.illumination = illumination
71
- self.geometry = geometry
46
+ """Percent of illumination (0 to 1)"""
47
+
48
+ geometry: ShapelyPolygon = None
49
+ """Shapely Polygon of the moon's extent. Right ascension coordinates are in degrees (0 to 360)."""
72
50
 
73
51
  @classmethod
74
52
  def get(
starplot/models/planet.py CHANGED
@@ -5,10 +5,9 @@ from typing import Iterator
5
5
  import numpy as np
6
6
 
7
7
  from skyfield.api import Angle, wgs84
8
- from shapely import Polygon
9
8
 
10
9
  from starplot.data import load
11
- from starplot.models.base import SkyObject
10
+ from starplot.models.base import SkyObject, ShapelyPolygon
12
11
  from starplot.geometry import circle
13
12
  from starplot.utils import dt_or_now
14
13
 
@@ -72,24 +71,9 @@ class Planet(SkyObject):
72
71
  apparent_size: float
73
72
  """Apparent size (degrees)"""
74
73
 
75
- geometry: Polygon = None
74
+ geometry: ShapelyPolygon = None
76
75
  """Shapely Polygon of the planet's extent. Right ascension coordinates are in degrees (0...360)."""
77
76
 
78
- def __init__(
79
- self,
80
- ra: float,
81
- dec: float,
82
- name: str,
83
- dt: datetime,
84
- apparent_size: float,
85
- geometry: Polygon = None,
86
- ) -> None:
87
- super().__init__(ra, dec)
88
- self.name = name
89
- self.dt = dt
90
- self.apparent_size = apparent_size
91
- self.geometry = geometry
92
-
93
77
  @classmethod
94
78
  def all(
95
79
  cls,
starplot/models/star.py CHANGED
@@ -1,11 +1,11 @@
1
1
  import math
2
- from typing import Optional, Union, Iterator
2
+ from typing import Optional, Union, Iterator, Any
3
3
 
4
4
  import numpy as np
5
- from shapely import Point
6
5
  from ibis import _
6
+ from pydantic import field_validator
7
7
 
8
- from starplot.models.base import SkyObject
8
+ from starplot.models.base import SkyObject, ShapelyPoint
9
9
  from starplot.data.stars import StarCatalog, load as _load_stars
10
10
 
11
11
 
@@ -38,38 +38,20 @@ class Star(SkyObject):
38
38
  flamsteed: Optional[int] = None
39
39
  """Flamsteed number, if available"""
40
40
 
41
- geometry: Point = None
41
+ geometry: ShapelyPoint = None
42
42
  """Shapely Point of the star's position. Right ascension coordinates are in degrees (0...360)."""
43
43
 
44
- def __init__(
45
- self,
46
- ra: float,
47
- dec: float,
48
- magnitude: float,
49
- bv: float = None,
50
- hip: int = None,
51
- name: str = None,
52
- tyc: str = None,
53
- ccdm: str = None,
54
- geometry: Point = None,
55
- constellation_id: str = None,
56
- bayer: str = None,
57
- flamsteed: int = None,
58
- ) -> None:
59
- super().__init__(ra, dec, constellation_id)
60
- self.magnitude = magnitude
61
- self.bv = bv
62
- self.hip = hip if hip is not None and np.isfinite(hip) else None
63
- self.name = name
64
- self.tyc = tyc
65
- self.ccdm = ccdm
66
- self.geometry = geometry
67
-
68
- if bayer:
69
- self.bayer = bayer
70
-
71
- if flamsteed and not math.isnan(flamsteed):
72
- self.flamsteed = int(flamsteed)
44
+ @field_validator("flamsteed", "hip", mode="before")
45
+ @classmethod
46
+ def nan(cls, value: int) -> int:
47
+ if not value or math.isnan(value):
48
+ return None
49
+
50
+ return int(value)
51
+
52
+ def model_post_init(self, context: Any) -> None:
53
+ self.bayer = self.bayer or None
54
+ self.hip = self.hip if self.hip is not None and np.isfinite(self.hip) else None
73
55
 
74
56
  def __repr__(self) -> str:
75
57
  return f"Star(hip={self.hip}, tyc={self.tyc}, magnitude={self.magnitude}, ra={self.ra}, dec={self.dec})"
@@ -83,7 +65,7 @@ class Star(SkyObject):
83
65
 
84
66
  @classmethod
85
67
  def get(
86
- cls, catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11, **kwargs
68
+ cls, catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11, sql: str = None, **kwargs
87
69
  ) -> Union["Star", None]:
88
70
  """
89
71
  Get a Star, by matching its attributes as specified in `**kwargs`
@@ -94,6 +76,7 @@ class Star(SkyObject):
94
76
 
95
77
  Args:
96
78
  catalog: The catalog of stars to use: "big-sky-mag11", or "big-sky" -- see [`StarCatalog`](/reference-data/#starplot.data.stars.StarCatalog) for details
79
+ sql: SQL query for selecting star (table name is "_")
97
80
  **kwargs: Attributes on the star you want to match
98
81
 
99
82
  Raises: `ValueError` if more than one star is matched
@@ -109,6 +92,7 @@ class Star(SkyObject):
109
92
  df = _load_stars(
110
93
  catalog=catalog,
111
94
  filters=filters,
95
+ sql=sql,
112
96
  ).to_pandas()
113
97
 
114
98
  results = [from_tuple(s) for s in df.itertuples()]
@@ -125,13 +109,17 @@ class Star(SkyObject):
125
109
 
126
110
  @classmethod
127
111
  def find(
128
- cls, where: list, catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11
112
+ cls,
113
+ where: list = None,
114
+ sql: str = None,
115
+ catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11,
129
116
  ) -> list["Star"]:
130
117
  """
131
118
  Find Stars
132
119
 
133
120
  Args:
134
121
  where: A list of expressions that determine which stars to find. See [Selecting Objects](/reference-selecting-objects/) for details.
122
+ sql: SQL query for selecting stars (table name is "_")
135
123
  catalog: The catalog of stars to use: "big-sky-mag11", or "big-sky" -- see [`StarCatalog`](/reference-data/#starplot.data.stars.StarCatalog) for details
136
124
 
137
125
  Returns:
@@ -141,6 +129,7 @@ class Star(SkyObject):
141
129
  df = _load_stars(
142
130
  catalog=catalog,
143
131
  filters=where,
132
+ sql=sql,
144
133
  ).to_pandas()
145
134
 
146
135
  return [from_tuple(s) for s in df.itertuples()]
@@ -157,10 +146,10 @@ def from_tuple(star: tuple) -> Star:
157
146
  ccdm=getattr(star, "ccdm", None),
158
147
  name=getattr(star, "name", None),
159
148
  geometry=star.geometry,
160
- constellation_id=getattr(star, "constellation", None),
161
149
  bayer=getattr(star, "bayer", None),
162
150
  flamsteed=getattr(star, "flamsteed", None),
163
151
  )
152
+ s._constellation_id = getattr(star, "constellation", None)
164
153
  s._row_id = getattr(star, "rowid", None)
165
154
 
166
155
  return s
starplot/models/sun.py CHANGED
@@ -2,10 +2,9 @@ from datetime import datetime
2
2
 
3
3
  import numpy as np
4
4
  from skyfield.api import Angle, wgs84
5
- from shapely import Polygon
6
5
 
7
6
  from starplot.data import load
8
- from starplot.models.base import SkyObject
7
+ from starplot.models.base import SkyObject, ShapelyPolygon
9
8
  from starplot.geometry import circle
10
9
  from starplot.utils import dt_or_now
11
10
 
@@ -22,24 +21,9 @@ class Sun(SkyObject):
22
21
  apparent_size: float
23
22
  """Apparent size (degrees)"""
24
23
 
25
- geometry: Polygon = None
24
+ geometry: ShapelyPolygon = None
26
25
  """Shapely Polygon of the Sun's extent. Right ascension coordinates are in degrees (0...360)."""
27
26
 
28
- def __init__(
29
- self,
30
- ra: float,
31
- dec: float,
32
- name: str,
33
- dt: datetime,
34
- apparent_size: float,
35
- geometry: Polygon = None,
36
- ) -> None:
37
- super().__init__(ra, dec)
38
- self.name = name
39
- self.dt = dt
40
- self.apparent_size = apparent_size
41
- self.geometry = geometry
42
-
43
27
  @classmethod
44
28
  def get(
45
29
  cls,
starplot/optic.py CHANGED
@@ -220,6 +220,7 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
220
220
  legend_label: str = "Star",
221
221
  bayer_labels: bool = False,
222
222
  flamsteed_labels: bool = False,
223
+ sql: str = None,
223
224
  *args,
224
225
  **kwargs,
225
226
  ):
@@ -239,6 +240,7 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
239
240
  legend_label: Label for stars in the legend. If `None`, then they will not be in the legend.
240
241
  bayer_labels: If True, then Bayer labels for stars will be plotted.
241
242
  flamsteed_labels: If True, then Flamsteed number labels for stars will be plotted.
243
+ sql: SQL query for selecting stars (table name is `_`). This query will be applied _after_ any filters in the `where` kwarg.
242
244
  """
243
245
  optic_star_multiplier = self.FIELD_OF_VIEW_MAX / self.optic.true_fov
244
246
  size_fn_mx = None
starplot/optics.py CHANGED
@@ -3,18 +3,15 @@ import math
3
3
  from abc import ABC, abstractmethod
4
4
 
5
5
  import pyproj
6
-
7
6
  from matplotlib import patches
7
+ from pydantic import BaseModel, computed_field
8
8
 
9
9
  from starplot.utils import in_circle
10
10
 
11
11
 
12
- class Optic(ABC):
12
+ class Optic(BaseModel, ABC):
13
13
  """Abstract class for defining Optics."""
14
14
 
15
- def __init__(self) -> "Optic":
16
- pass
17
-
18
15
  def __str__(self):
19
16
  return "Optic"
20
17
 
@@ -70,18 +67,34 @@ class Scope(Optic):
70
67
  Scope: A new instance of a Scope optic
71
68
  """
72
69
 
73
- def __init__(
74
- self, focal_length: float, eyepiece_focal_length: float, eyepiece_fov: float
75
- ) -> "Scope":
76
- self.focal_length = focal_length
77
- self.eyepiece_focal_length = eyepiece_focal_length
78
- self.eyepiece_fov = eyepiece_fov
79
- self.magnification = self.focal_length / self.eyepiece_focal_length
80
- self.true_fov = self.eyepiece_fov / self.magnification
81
- self.radius = self._compute_radius(self.true_fov / 2)
70
+ focal_length: float
71
+ """Focal length (mm) of the telescope"""
72
+
73
+ eyepiece_focal_length: float
74
+ """Focal length (mm) of the eyepiece"""
75
+
76
+ eyepiece_fov: float
77
+ """Field of view (degrees) of the eyepiece"""
78
+
79
+ @computed_field
80
+ @property
81
+ def magnification(self) -> float:
82
+ """Magnification calculated from the telescope's focal length and eyepiece focal length"""
83
+ return self.focal_length / self.eyepiece_focal_length
84
+
85
+ @computed_field
86
+ @property
87
+ def true_fov(self) -> float:
88
+ """True field of view of telescope"""
89
+ return self.eyepiece_fov / self.magnification
90
+
91
+ @computed_field
92
+ @property
93
+ def radius(self) -> float:
94
+ return self._compute_radius(self.true_fov / 2)
82
95
 
83
96
  def __str__(self):
84
- return f"{self.focal_length}mm w/ {self.eyepiece_focal_length}mm ({self.magnification:.0f}x) @ {self.eyepiece_fov:.0f}\N{DEGREE SIGN} = {self.true_fov:.2f}\N{DEGREE SIGN} TFOV"
97
+ return f"{self.focal_length:.0f}mm w/ {self.eyepiece_focal_length:.0f}mm ({self.magnification:.0f}x) @ {self.eyepiece_fov:.0f}\N{DEGREE SIGN} = {self.true_fov:.2f}\N{DEGREE SIGN} TFOV"
85
98
 
86
99
  @property
87
100
  def xlim(self):
@@ -172,14 +185,25 @@ class Binoculars(Optic):
172
185
 
173
186
  """
174
187
 
175
- def __init__(self, magnification: float, fov: float) -> "Binoculars":
176
- self.magnification = magnification
177
- self.apparent_fov = fov
178
- self.true_fov = self.apparent_fov / self.magnification
179
- self.radius = self._compute_radius(self.true_fov / 2)
188
+ magnification: float
189
+ """Magnification of the binoculars"""
190
+
191
+ fov: float
192
+ """Apparent field of view of the binoculars"""
193
+
194
+ @computed_field
195
+ @property
196
+ def true_fov(self) -> float:
197
+ """True field of view of binoculars"""
198
+ return self.fov / self.magnification
199
+
200
+ @computed_field
201
+ @property
202
+ def radius(self) -> float:
203
+ return self._compute_radius(self.true_fov / 2)
180
204
 
181
205
  def __str__(self):
182
- return f"{self.magnification}x @ {self.apparent_fov}\N{DEGREE SIGN} = {self.true_fov}\N{DEGREE SIGN}"
206
+ return f"{self.magnification:.0f}x @ {self.fov:.0f}\N{DEGREE SIGN} = {self.true_fov}\N{DEGREE SIGN}"
183
207
 
184
208
  @property
185
209
  def xlim(self):
@@ -225,39 +249,56 @@ class Camera(Optic):
225
249
  sensor_height: Height of camera sensor (mm)
226
250
  sensor_width: Width of camera sensor (mm)
227
251
  lens_focal_length: Focal length of camera lens (mm)
228
- rotation: Angle (degrees) to rotate camera
252
+ rotation: Angle (degrees) to rotate camera, defaults to 0
229
253
 
230
254
  Returns:
231
255
  Camera: A new instance of a Camera optic
232
256
 
233
257
  """
234
258
 
235
- def __init__(
236
- self,
237
- sensor_height: float,
238
- sensor_width: float,
239
- lens_focal_length: float,
240
- rotation: float = 0,
241
- ) -> "Camera":
242
- self.sensor_height = sensor_height
243
- self.sensor_width = sensor_width
244
- self.lens_focal_length = lens_focal_length
245
-
246
- self.true_fov_x = 2 * math.degrees(
259
+ sensor_height: float
260
+ """Height (mm) of camera's sensor"""
261
+
262
+ sensor_width: float
263
+ """Width (mm) of camera's sensor"""
264
+
265
+ lens_focal_length: float
266
+ """Focal length (mm) of the camera's lens"""
267
+
268
+ rotation: float = 0
269
+ """Angle (degrees) to rotate the camera"""
270
+
271
+ @computed_field
272
+ @property
273
+ def true_fov_x(self) -> float:
274
+ return 2 * math.degrees(
247
275
  math.atan(self.sensor_width / (2 * self.lens_focal_length))
248
276
  )
249
- self.true_fov_y = 2 * math.degrees(
277
+
278
+ @computed_field
279
+ @property
280
+ def true_fov_y(self) -> float:
281
+ return 2 * math.degrees(
250
282
  math.atan(self.sensor_height / (2 * self.lens_focal_length))
251
283
  )
252
- self.true_fov = max(self.true_fov_x, self.true_fov_y)
253
284
 
254
- self.radius_x = self._compute_radius(self.true_fov_x / 2)
255
- self.radius_y = self._compute_radius(self.true_fov_y / 2)
285
+ @computed_field
286
+ @property
287
+ def true_fov(self) -> float:
288
+ return max(self.true_fov_x, self.true_fov_y)
289
+
290
+ @computed_field
291
+ @property
292
+ def radius_x(self) -> float:
293
+ return self._compute_radius(self.true_fov_x / 2)
256
294
 
257
- self.rotation = rotation
295
+ @computed_field
296
+ @property
297
+ def radius_y(self) -> float:
298
+ return self._compute_radius(self.true_fov_y / 2)
258
299
 
259
300
  def __str__(self):
260
- return f"{self.sensor_width}x{self.sensor_height} w/ {self.lens_focal_length}mm lens = {self.true_fov_x:.2f}\N{DEGREE SIGN} x {self.true_fov_y:.2f}\N{DEGREE SIGN}"
301
+ return f"{self.sensor_width}x{self.sensor_height} w/ {self.lens_focal_length:.0f}mm lens = {self.true_fov_x:.2f}\N{DEGREE SIGN} x {self.true_fov_y:.2f}\N{DEGREE SIGN}"
261
302
 
262
303
  @property
263
304
  def xlim(self):
@@ -65,6 +65,7 @@ class ConstellationPlotterMixin:
65
65
  self,
66
66
  style: LineStyle = None,
67
67
  where: list = None,
68
+ sql: str = None,
68
69
  ):
69
70
  """Plots the constellation lines **only**. To plot constellation borders and/or labels, see separate functions for them.
70
71
 
@@ -73,6 +74,7 @@ class ConstellationPlotterMixin:
73
74
  Args:
74
75
  style: Styling of the constellations. If None, then the plot's style (specified when creating the plot) will be used
75
76
  where: A list of expressions that determine which constellations to plot. See [Selecting Objects](/reference-selecting-objects/) for details.
77
+ sql: SQL query for selecting constellations (table name is `_`). This query will be applied _after_ any filters in the `where` kwarg.
76
78
  """
77
79
  self.logger.debug("Plotting constellation lines...")
78
80
 
@@ -80,7 +82,7 @@ class ConstellationPlotterMixin:
80
82
  ctr = 0
81
83
 
82
84
  extent = self._extent_mask()
83
- results = condata.load(extent=extent, filters=where)
85
+ results = condata.load(extent=extent, filters=where, sql=sql)
84
86
  constellations_df = results.to_pandas()
85
87
 
86
88
  if constellations_df.empty:
starplot/plotters/dsos.py CHANGED
@@ -44,7 +44,7 @@ class DsoPlotterMixin:
44
44
  This is just a small wrapper around the `dsos()` function, so any `kwargs` will be passed through.
45
45
  """
46
46
  where = kwargs.pop("where", [])
47
- where.append(_.type == DsoType.OPEN_CLUSTER)
47
+ where.append(_.type == DsoType.OPEN_CLUSTER.value)
48
48
  self.dsos(where=where, **kwargs)
49
49
 
50
50
  def globular_clusters(self, **kwargs):
@@ -54,7 +54,7 @@ class DsoPlotterMixin:
54
54
  This is just a small wrapper around the `dsos()` function, so any `kwargs` will be passed through.
55
55
  """
56
56
  where = kwargs.pop("where", [])
57
- where.append(_.type == DsoType.GLOBULAR_CLUSTER)
57
+ where.append(_.type == DsoType.GLOBULAR_CLUSTER.value)
58
58
  self.dsos(where=where, **kwargs)
59
59
 
60
60
  def galaxies(self, **kwargs):
@@ -68,9 +68,9 @@ class DsoPlotterMixin:
68
68
  This is just a small wrapper around the `dsos()` function, so any `kwargs` will be passed through.
69
69
  """
70
70
  galaxy_types = [
71
- DsoType.GALAXY,
72
- DsoType.GALAXY_PAIR,
73
- DsoType.GALAXY_TRIPLET,
71
+ DsoType.GALAXY.value,
72
+ DsoType.GALAXY_PAIR.value,
73
+ DsoType.GALAXY_TRIPLET.value,
74
74
  ]
75
75
  where = kwargs.pop("where", [])
76
76
  where.append(_.type.isin(galaxy_types))
@@ -92,12 +92,12 @@ class DsoPlotterMixin:
92
92
  This is just a small wrapper around the `dsos()` function, so any `kwargs` will be passed through.
93
93
  """
94
94
  nebula_types = [
95
- DsoType.NEBULA,
96
- DsoType.PLANETARY_NEBULA,
97
- DsoType.EMISSION_NEBULA,
98
- DsoType.STAR_CLUSTER_NEBULA,
99
- DsoType.REFLECTION_NEBULA,
100
- DsoType.HII_IONIZED_REGION,
95
+ DsoType.NEBULA.value,
96
+ DsoType.PLANETARY_NEBULA.value,
97
+ DsoType.EMISSION_NEBULA.value,
98
+ DsoType.STAR_CLUSTER_NEBULA.value,
99
+ DsoType.REFLECTION_NEBULA.value,
100
+ DsoType.HII_IONIZED_REGION.value,
101
101
  ]
102
102
  where = kwargs.pop("where", [])
103
103
  where.append(_.type.isin(nebula_types))
@@ -113,6 +113,8 @@ class DsoPlotterMixin:
113
113
  legend_labels: Mapping[DsoType, str] = DSO_LEGEND_LABELS,
114
114
  alpha_fn: Callable[[DSO], float] = None,
115
115
  label_fn: Callable[[DSO], str] = None,
116
+ sql: str = None,
117
+ sql_labels: str = None,
116
118
  ):
117
119
  """
118
120
  Plots Deep Sky Objects (DSOs), from OpenNGC
@@ -125,6 +127,8 @@ class DsoPlotterMixin:
125
127
  legend_labels: A dictionary that maps a `DsoType` to the legend label that'll be plotted for that type of DSO. If you want to hide all DSO legend labels, then set this arg to `None`.
126
128
  alpha_fn: Callable for calculating the alpha value (aka "opacity") of each DSO. If `None`, then the marker style's alpha will be used.
127
129
  label_fn: Callable for determining the label of each DSO. If `None`, then the names in the `labels` kwarg will be used.
130
+ sql: SQL query for selecting DSOs (table name is `_`). This query will be applied _after_ any filters in the `where` kwarg.
131
+ sql_labels: SQL query for selecting DSOs that will be labeled (table name is `_`). Applied _after_ any filters in the `where_labels` kwarg.
128
132
  """
129
133
 
130
134
  # TODO: add kwarg styles
@@ -143,12 +147,19 @@ class DsoPlotterMixin:
143
147
  legend_labels = {**DSO_LEGEND_LABELS, **legend_labels}
144
148
 
145
149
  extent = self._extent_mask()
146
- dso_results = load(extent=extent, filters=where)
150
+ dso_results = load(extent=extent, filters=where, sql=sql)
147
151
 
148
152
  dso_results_labeled = dso_results
149
153
  for f in where_labels:
150
154
  dso_results_labeled = dso_results_labeled.filter(f)
151
155
 
156
+ if sql_labels:
157
+ result = (
158
+ dso_results_labeled.alias("_").sql(sql_labels).select("sk").execute()
159
+ )
160
+ skids = result["sk"].to_list()
161
+ dso_results_labeled = dso_results_labeled.filter(_.sk.isin(skids))
162
+
152
163
  label_row_ids = dso_results_labeled.to_pandas()["rowid"].tolist()
153
164
 
154
165
  results_df = dso_results.to_pandas()
@@ -2,6 +2,7 @@ from typing import Callable, Mapping
2
2
 
3
3
  import rtree
4
4
  import numpy as np
5
+ from ibis import _ as ibis_table
5
6
  from skyfield.api import Star as SkyfieldStar, wgs84
6
7
 
7
8
  from starplot import callables
@@ -13,13 +14,14 @@ from starplot.profile import profile
13
14
 
14
15
 
15
16
  class StarPlotterMixin:
16
- def _load_stars(self, catalog, filters=None):
17
+ def _load_stars(self, catalog, filters=None, sql=None):
17
18
  extent = self._extent_mask()
18
19
 
19
20
  return stars.load(
20
21
  extent=extent,
21
22
  catalog=catalog,
22
23
  filters=filters,
24
+ sql=sql,
23
25
  )
24
26
 
25
27
  def _scatter_stars(self, ras, decs, sizes, alphas, colors, style=None, **kwargs):
@@ -165,6 +167,8 @@ class StarPlotterMixin:
165
167
  legend_label: str = "Star",
166
168
  bayer_labels: bool = False,
167
169
  flamsteed_labels: bool = False,
170
+ sql: str = None,
171
+ sql_labels: str = None,
168
172
  *args,
169
173
  **kwargs,
170
174
  ):
@@ -190,6 +194,8 @@ class StarPlotterMixin:
190
194
  legend_label: Label for stars in the legend. If `None`, then they will not be in the legend.
191
195
  bayer_labels: If True, then Bayer labels for stars will be plotted.
192
196
  flamsteed_labels: If True, then Flamsteed number labels for stars will be plotted.
197
+ sql: SQL query for selecting stars (table name is `_`). This query will be applied _after_ any filters in the `where` kwarg.
198
+ sql_labels: SQL query for selecting stars that will be labeled (table name is `_`). Applied _after_ any filters in the `where_labels` kwarg.
193
199
  """
194
200
 
195
201
  # fallback to style if callables are None
@@ -205,12 +211,21 @@ class StarPlotterMixin:
205
211
  stars_to_index = []
206
212
  labels = labels or {}
207
213
 
208
- star_results = self._load_stars(catalog, filters=where)
214
+ star_results = self._load_stars(catalog, filters=where, sql=sql)
209
215
 
210
216
  star_results_labeled = star_results
211
217
  for f in where_labels:
212
218
  star_results_labeled = star_results_labeled.filter(f)
213
219
 
220
+ if sql_labels:
221
+ result = (
222
+ star_results_labeled.alias("_").sql(sql_labels).select("sk").execute()
223
+ )
224
+ skids = result["sk"].to_list()
225
+ star_results_labeled = star_results_labeled.filter(
226
+ ibis_table.sk.isin(skids)
227
+ )
228
+
214
229
  label_row_ids = star_results_labeled.to_pandas()["rowid"].tolist()
215
230
 
216
231
  stars_df = star_results.to_pandas()
starplot/styles/base.py CHANGED
@@ -169,6 +169,7 @@ class MarkerSymbolEnum(str, Enum):
169
169
  MarkerSymbolEnum.CIRCLE_DOTTED_RINGS: circle_dotted_rings(),
170
170
  MarkerSymbolEnum.CIRCLE_LINE: circle_line(),
171
171
  MarkerSymbolEnum.COMET: "$\u2604$",
172
+ MarkerSymbolEnum.STAR_4: "$\u2726$",
172
173
  MarkerSymbolEnum.STAR_8: "$\u2734$",
173
174
  MarkerSymbolEnum.ELLIPSE: ellipse(),
174
175
  }[self.value]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: starplot
3
- Version: 0.15.6
3
+ Version: 0.15.8
4
4
  Summary: Star charts and maps of the sky
5
5
  Keywords: astronomy,stars,charts,maps,constellations,sky,plotting
6
6
  Author-email: Steve Berardi <hello@steveberardi.com>
@@ -1,4 +1,4 @@
1
- starplot/__init__.py,sha256=HcBm5p7wfCcnsDaBPmpDAwcunStcLJSiZPoKfHZ2qUI,558
1
+ starplot/__init__.py,sha256=vOqisTpc-0yKEJR9jKWJKoQC__x8vg_PRaKi8MT9Cgc,558
2
2
  starplot/base.py,sha256=s-mixdIBeT5QcKYuGXy52oBrg7wJO2rpmHmXqorex1c,43223
3
3
  starplot/callables.py,sha256=_zDGCAJTqqNLvCtcIt4PVEe2L0Ggvl6pj-7ZFI-0zqI,4043
4
4
  starplot/cli.py,sha256=W-V-h1DeD8zwzdhYuQ-i26LSZWYcS_NJ0zRXa-dmy1o,711
@@ -8,8 +8,8 @@ starplot/geometry.py,sha256=wr1vJo-WWP51bfblVC2CAR7288yWrG31dZg9ks9vfYg,4359
8
8
  starplot/horizon.py,sha256=vsJCsJE5VJEe0vlx6A-L70LAGjbey9xMjgUcRSj1F4Y,15808
9
9
  starplot/map.py,sha256=CIJC2CoAhH4SlV-dgLC4xDqCoyqXl7DOGegZafRIoqU,23441
10
10
  starplot/mixins.py,sha256=8X-LhFbsVNBZN1PKIjlftknppRpcQMOMYE15gdNC-sE,4857
11
- starplot/optic.py,sha256=NlZZBHRX13KHingNiofv6XDXWUZ4yKH7StBUHuSX_vc,15406
12
- starplot/optics.py,sha256=JfSzfrCx_g8r3upyukgJUtXekwyVkCJ3dZxdOclfzU4,8624
11
+ starplot/optic.py,sha256=aj7Ks8np6f6aefqGtNHbxk3mwcUydhNAsZAo5qO2OBU,15568
12
+ starplot/optics.py,sha256=v94Ff8bIruVBrME7lzwORlayadpoFIGQsAK0sFlx43Y,9314
13
13
  starplot/profile.py,sha256=V5LOZFDdnGo-P8ikWvV3jmUVJIKO3gd4H2bjBlk7aUM,300
14
14
  starplot/projections.py,sha256=o5wHLoaU8o0s0Z2kkWKxeNbOrskvWG88ULAXDoLOGeA,3423
15
15
  starplot/settings.py,sha256=DCylQwkrgUki1yHrelMF483t98NIDQ1bivCeWfeknJw,627
@@ -19,32 +19,32 @@ starplot/data/__init__.py,sha256=3M_uHlwyIsc9cyfabnOp-6CoXoMteYAaWuj4St9_1BE,308
19
19
  starplot/data/bigsky.py,sha256=SiHzzhoy-DAZpJ-Q_-lmJqHRLwTKgosaVOEXYWQ94PI,2820
20
20
  starplot/data/constellation_lines.py,sha256=RLyFSoxGRL7kj9gGT4DDUubClKZEDu5wGUMG3PlpYfY,19344
21
21
  starplot/data/constellation_stars.py,sha256=l2GeyJWuVtzH-MIw1oGR6jeMBvfqX-q-S2V_ilcEZG8,38221
22
- starplot/data/constellations.py,sha256=E-wEyZ4W5lcUS_StH26F9NMRsFyFs-utGxGcfRK7zBk,15484
22
+ starplot/data/constellations.py,sha256=05fjaLy5gBDQYyxMV_Etq2O2OMzOh0ozYWT_BwNmfMc,15745
23
23
  starplot/data/db.py,sha256=77oWr2HkZ95K_mG4vLc-ivP_SOFIiFYwoqrtTw-YWtk,384
24
- starplot/data/dsos.py,sha256=tx-6oDzKPthg-nxfda2ah5t10USdyrckP4UjM5k-e10,1237
24
+ starplot/data/dsos.py,sha256=Tc264aSA3J8eVmVTUhjrMYMkijP870dqaleMk7xr3hE,1509
25
25
  starplot/data/ecliptic.py,sha256=Qre9YdFbTC9mAx-vd2C0Ou4CsnRehIScnTpmEUDDYcM,4638
26
- starplot/data/stars.py,sha256=iM0zWqjQe8L8rAekFY0FtPXRutnK6Nv4jhoV6m8983g,12077
26
+ starplot/data/stars.py,sha256=ImcD0lwGgnOlVICQyM62nXVHoSvkS82x1TNIldFprCU,12288
27
27
  starplot/data/utils.py,sha256=RPk3bnfL-KtjMk1VQygDD27INz_gEya_B1hu7X4K8hU,772
28
28
  starplot/data/library/bigsky.0.4.0.stars.mag11.parquet,sha256=-_D6bghArUh1cmOKktxmmBFQNThiCWjVleI0wduP1GI,38884673
29
29
  starplot/data/library/de421_2001.bsp,sha256=ymkZigAd8Vgscq_DYkdR4nZ1VGD5wwPd-sxe6HiiTns,5341104
30
- starplot/data/library/sky.db,sha256=gbige0EAs5meX_x3DWPuKO23KiKeujGfIh61pfh_OG4,38023168
30
+ starplot/data/library/sky.db,sha256=yl-pokiPUgHvlek7HhAbURwz4NFQ8N7WgX0b1oYjYrk,38023168
31
31
  starplot/models/__init__.py,sha256=qi4arVatzEcR9TAbHadSbhq8xRhSb1_Br3UKNv4FP7o,330
32
- starplot/models/base.py,sha256=zuHcRSs4eSyIwtyUTsAk9a73bnIW2gfWh7Ze_zt8Tq0,1623
33
- starplot/models/constellation.py,sha256=qfBcWyQRL2m83wNIKiPk-4bGRblYvabxymW6TJ0nXFI,3355
34
- starplot/models/dso.py,sha256=KHd5N6F2Y7EhO3RWUpmjfl_162uwNqhlC1Zqh9p0Ev4,6942
35
- starplot/models/moon.py,sha256=FNIzdSMV-5ET7WtmSxWz_4n-U4IlIomQpGTbOMl7fxk,5104
32
+ starplot/models/base.py,sha256=sQ_qTUOUfGxtgLdJVnSbMG328_d_AO5GSIcnC-e0m80,2681
33
+ starplot/models/constellation.py,sha256=ayIGfafFs2eymWHlFdDgZPJkx115x_avPC1egqVZWnI,3220
34
+ starplot/models/dso.py,sha256=ZcAlMn5-y3A_IxfCjhMxmmQzSBeXbxb_P-Jw8T4DQPI,7022
35
+ starplot/models/moon.py,sha256=dW0N70DFx4yO9Zawfo96Z3LZxzLTMZDDpBHrP9rqfa0,4546
36
36
  starplot/models/objects.py,sha256=BXwUMT-zPiOYBWYV7L-TRDfPo6lsx_AIk3yxJ3qi0ck,683
37
- starplot/models/planet.py,sha256=QekNlSRw6tv5-TY8qUFvtLKxmz7ao6nv3Iwl7FiGeHY,5014
38
- starplot/models/star.py,sha256=7LPlqZInBdu4D5XH2sJDiM6IJvtyVDAZvijMMH5hGAY,4693
39
- starplot/models/sun.py,sha256=iUforZSmzw-o_tOKHgYslhFHUQTQxllDKMG1HdzlI98,2695
37
+ starplot/models/planet.py,sha256=TyPGMQnlq4NwMXNSRrIgbpL4rAWTdKyNeo5jpSR0Crg,4661
38
+ starplot/models/star.py,sha256=gw36P1BB9qq4IfvS4TqcZsmltKeNWvcnw87wSDxmE1w,4555
39
+ starplot/models/sun.py,sha256=3EaRJclmYX-jhSJav5nGCazH-9_YHT6Qr2irDSsC63I,2342
40
40
  starplot/plotters/__init__.py,sha256=p2wpyY_jK5_zOWWbGtokw4tcHBTuILjAhuobghDvdvM,223
41
- starplot/plotters/constellations.py,sha256=IDVVfEsNPbPC4E692bgr-bQqiV8mQuu18BFZKYHMV_k,13094
42
- starplot/plotters/dsos.py,sha256=AtqhphVOfjuYZtxmgF18d3jzTYaIv2j6xnZCO9anpds,8406
41
+ starplot/plotters/constellations.py,sha256=ZkI3VfbI7yZfuzg3vG8de4AdxzolXgRj8gBDzwfiHo0,13274
42
+ starplot/plotters/dsos.py,sha256=u_V6b9ADk2YnqwXM6YNayju4SWxe1kLKKT-cMLJva5Y,9095
43
43
  starplot/plotters/experimental.py,sha256=P4T9jJyAnViv6k2RJpmYEY8uI-0dyd-E6NeIRUWbu6c,5909
44
44
  starplot/plotters/milkyway.py,sha256=ofenSqpqeeg7_LlWnH1PJiiHstzD-qQL7Lk6-WlEv2M,1122
45
- starplot/plotters/stars.py,sha256=AB3O16Ql38z3_ityAJJljzRtnVixvZ22kaJAXqj4hNs,11710
45
+ starplot/plotters/stars.py,sha256=4k_OUMiNuGT28cMZfm-R9kMzUptPgtW55iqB9vH9WBo,12441
46
46
  starplot/styles/__init__.py,sha256=rtwzAylENUGIYGDPl106RGjU6e89yNURoxmPD3I_HM0,193
47
- starplot/styles/base.py,sha256=fuj_hWqF5xViJMhisyRCh88YRNHCcvxIqGeBlBIm6cw,36405
47
+ starplot/styles/base.py,sha256=eSVITtg_dGp8ChI4pm4g3glpiW_Pd0irwNGQp9HND3g,36454
48
48
  starplot/styles/extensions.py,sha256=Mv9FTSbwEtB4IFuo_5MFDlHRYQsKdC-19uLYtmJSUuE,649
49
49
  starplot/styles/fonts.py,sha256=wC3cHuFkBUaZM5fKpT_ExV7anrRKMJX46mjEfcSRQMU,379
50
50
  starplot/styles/helpers.py,sha256=AGgHWaHLzJZ6jicvwPzY-p5oSHE0H8gDk1raCmeRFtg,3032
@@ -92,8 +92,8 @@ starplot/styles/fonts-library/inter/Inter-SemiBoldItalic.ttf,sha256=HhKJRT16iVz7
92
92
  starplot/styles/fonts-library/inter/Inter-Thin.ttf,sha256=TDktzIrZFvD533VZq1VjsB3ZT587LbNGF_45LgAGAzk,403404
93
93
  starplot/styles/fonts-library/inter/Inter-ThinItalic.ttf,sha256=X8Ca-UpEf65vgsAPFd-u-ernxWDmy-RtPoRSQBmldKo,410232
94
94
  starplot/styles/fonts-library/inter/LICENSE.txt,sha256=JiSB6ERSGzJvXs0FPlm5jIstp4yO4b27boF0MF5Uk1o,4380
95
- starplot-0.15.6.dist-info/entry_points.txt,sha256=Sm6jC6h_RcaMGC8saLnYmT0SdhcF9_rMeQIiHneLHyc,46
96
- starplot-0.15.6.dist-info/licenses/LICENSE,sha256=jcjClHF4BQwhz-kDgia-KphO9Zxu0rCa2BbiA7j1jeU,1070
97
- starplot-0.15.6.dist-info/WHEEL,sha256=Dyt6SBfaasWElUrURkknVFAZDHSTwxg3PaTza7RSbkY,100
98
- starplot-0.15.6.dist-info/METADATA,sha256=dirAqiVfN0z6zfJ62tMAssHoQEBDULtzfms5WWM2Ijs,4276
99
- starplot-0.15.6.dist-info/RECORD,,
95
+ starplot-0.15.8.dist-info/entry_points.txt,sha256=Sm6jC6h_RcaMGC8saLnYmT0SdhcF9_rMeQIiHneLHyc,46
96
+ starplot-0.15.8.dist-info/licenses/LICENSE,sha256=jcjClHF4BQwhz-kDgia-KphO9Zxu0rCa2BbiA7j1jeU,1070
97
+ starplot-0.15.8.dist-info/WHEEL,sha256=Dyt6SBfaasWElUrURkknVFAZDHSTwxg3PaTza7RSbkY,100
98
+ starplot-0.15.8.dist-info/METADATA,sha256=w1KjXfQySDtPeNVmkQRP2HZsQD8TxlQwtSWVGW4wK3w,4276
99
+ starplot-0.15.8.dist-info/RECORD,,