BREAKING CHANGE: name of objects of type `kosmorrolib.model.Object` has been replaced with an enum typed identifier.tags/v0.11.0
@@ -3,4 +3,4 @@ | |||
from .model import Position, Event, AsterEphemerides, Object | |||
from .ephemerides import get_ephemerides, get_moon_phase | |||
from .events import get_events | |||
from .enum import EventType, MoonPhaseType | |||
from .enum import * |
@@ -12,3 +12,21 @@ def translate_to_timezone(date: datetime, to_tz: int, from_tz: int = None): | |||
return date.replace(tzinfo=source_tz).astimezone( | |||
tz=timezone(timedelta(hours=to_tz)) | |||
) | |||
def normalize_datetime(date: datetime) -> datetime: | |||
"""Round the seconds in the given datetime | |||
>>> normalize_datetime(datetime(2021, 6, 9, 2, 30, 29)) | |||
datetime.datetime(2021, 6, 9, 2, 30) | |||
>>> normalize_datetime(datetime(2021, 6, 9, 2, 30, 30)) | |||
datetime.datetime(2021, 6, 9, 2, 31) | |||
""" | |||
return datetime( | |||
date.year, | |||
date.month, | |||
date.day, | |||
date.hour, | |||
date.minute if date.second < 30 else date.minute + 1, | |||
) |
@@ -25,3 +25,28 @@ class EventType(Enum): | |||
MAXIMAL_ELONGATION = 4 | |||
MOON_PERIGEE = 5 | |||
MOON_APOGEE = 6 | |||
class ObjectType(Enum): | |||
"""An enumeration of object types""" | |||
STAR = 0 | |||
PLANET = 1 | |||
DWARF_PLANET = 11 | |||
SATELLITE = 2 | |||
class ObjectIdentifier(Enum): | |||
"""An enumeration of identifiers for objects""" | |||
SUN = 0 | |||
EARTH = 1 | |||
MOON = 11 | |||
MERCURY = 2 | |||
VENUS = 3 | |||
MARS = 4 | |||
JUPITER = 5 | |||
SATURN = 6 | |||
URANUS = 7 | |||
NEPTUNE = 8 | |||
PLUTO = 9 |
@@ -9,7 +9,7 @@ from skyfield.constants import tau | |||
from skyfield.errors import EphemerisRangeError | |||
from .model import Position, AsterEphemerides, MoonPhase, Object, ASTERS | |||
from .dateutil import translate_to_timezone | |||
from .dateutil import translate_to_timezone, normalize_datetime | |||
from .core import get_skf_objects, get_timescale, get_iau2000b | |||
from .enum import MoonPhaseType | |||
from .exceptions import OutOfRangeDateError | |||
@@ -121,12 +121,12 @@ def get_ephemerides( | |||
>>> pos = Position(50.5824, 3.0624) | |||
>>> get_ephemerides(pos, datetime.date(2021, 6, 9)) | |||
[<AsterEphemerides rise_time=2021-06-09 03:36:18+00:00 culmination_time=2021-06-09 11:47:05+00:00 set_time=2021-06-09 19:58:10+00:00 aster=<Object type=star name=Sun />>, <AsterEphemerides rise_time=2021-06-09 02:58:49+00:00 culmination_time=2021-06-09 11:02:18+00:00 set_time=2021-06-09 19:15:58+00:00 aster=<Object type=satellite name=Moon />>, <AsterEphemerides rise_time=2021-06-09 04:05:32+00:00 culmination_time=2021-06-09 11:57:51+00:00 set_time=2021-06-09 19:49:16+00:00 aster=<Object type=planet name=Mercury />>, <AsterEphemerides rise_time=2021-06-09 04:51:39+00:00 culmination_time=2021-06-09 13:12:45+00:00 set_time=2021-06-09 21:33:38+00:00 aster=<Object type=planet name=Venus />>, <AsterEphemerides rise_time=2021-06-09 06:37:42+00:00 culmination_time=2021-06-09 14:39:49+00:00 set_time=2021-06-09 22:41:29+00:00 aster=<Object type=planet name=Mars />>, <AsterEphemerides rise_time=2021-06-09 23:43:15+00:00 culmination_time=2021-06-09 04:53:50+00:00 set_time=2021-06-09 10:00:35+00:00 aster=<Object type=planet name=Jupiter />>, <AsterEphemerides rise_time=2021-06-09 23:01:34+00:00 culmination_time=2021-06-09 03:40:38+00:00 set_time=2021-06-09 08:15:43+00:00 aster=<Object type=planet name=Saturn />>, <AsterEphemerides rise_time=2021-06-09 01:55:34+00:00 culmination_time=2021-06-09 09:18:00+00:00 set_time=2021-06-09 16:40:29+00:00 aster=<Object type=planet name=Uranus />>, <AsterEphemerides rise_time=2021-06-09 00:26:53+00:00 culmination_time=2021-06-09 06:12:55+00:00 set_time=2021-06-09 11:58:57+00:00 aster=<Object type=planet name=Neptune />>, <AsterEphemerides rise_time=2021-06-09 22:22:10+00:00 culmination_time=2021-06-09 02:31:56+00:00 set_time=2021-06-09 06:37:43+00:00 aster=<Object type=planet name=Pluto />>] | |||
[<AsterEphemerides rise_time=2021-06-09 03:36:00 culmination_time=2021-06-09 11:47:00 set_time=2021-06-09 19:58:00 aster=<Object type=STAR name=SUN />>, <AsterEphemerides rise_time=2021-06-09 02:59:00 culmination_time=2021-06-09 11:02:00 set_time=2021-06-09 19:16:00 aster=<Object type=SATELLITE name=MOON />>, <AsterEphemerides rise_time=2021-06-09 04:06:00 culmination_time=2021-06-09 11:58:00 set_time=2021-06-09 19:49:00 aster=<Object type=PLANET name=MERCURY />>, <AsterEphemerides rise_time=2021-06-09 04:52:00 culmination_time=2021-06-09 13:13:00 set_time=2021-06-09 21:34:00 aster=<Object type=PLANET name=VENUS />>, <AsterEphemerides rise_time=2021-06-09 06:38:00 culmination_time=2021-06-09 14:40:00 set_time=2021-06-09 22:41:00 aster=<Object type=PLANET name=MARS />>, <AsterEphemerides rise_time=2021-06-09 23:43:00 culmination_time=2021-06-09 04:54:00 set_time=2021-06-09 10:01:00 aster=<Object type=PLANET name=JUPITER />>, <AsterEphemerides rise_time=2021-06-09 23:02:00 culmination_time=2021-06-09 03:41:00 set_time=2021-06-09 08:16:00 aster=<Object type=PLANET name=SATURN />>, <AsterEphemerides rise_time=2021-06-09 01:56:00 culmination_time=2021-06-09 09:18:00 set_time=2021-06-09 16:40:00 aster=<Object type=PLANET name=URANUS />>, <AsterEphemerides rise_time=2021-06-09 00:27:00 culmination_time=2021-06-09 06:13:00 set_time=2021-06-09 11:59:00 aster=<Object type=PLANET name=NEPTUNE />>, <AsterEphemerides rise_time=2021-06-09 22:22:00 culmination_time=2021-06-09 02:32:00 set_time=2021-06-09 06:38:00 aster=<Object type=PLANET name=PLUTO />>] | |||
Compute the ephemerides for June 9th, 2021: | |||
>>> get_ephemerides(pos, datetime.date(2021, 6, 9), timezone=2) | |||
[<AsterEphemerides rise_time=2021-06-09 05:36:18+02:00 culmination_time=2021-06-09 13:47:05+02:00 set_time=2021-06-09 21:58:10+02:00 aster=<Object type=star name=Sun />>, <AsterEphemerides rise_time=2021-06-09 04:58:49+02:00 culmination_time=2021-06-09 13:02:19+02:00 set_time=2021-06-09 21:15:58+02:00 aster=<Object type=satellite name=Moon />>, <AsterEphemerides rise_time=2021-06-09 06:05:32+02:00 culmination_time=2021-06-09 13:57:51+02:00 set_time=2021-06-09 21:49:16+02:00 aster=<Object type=planet name=Mercury />>, <AsterEphemerides rise_time=2021-06-09 06:51:39+02:00 culmination_time=2021-06-09 15:12:45+02:00 set_time=2021-06-09 23:33:38+02:00 aster=<Object type=planet name=Venus />>, <AsterEphemerides rise_time=2021-06-09 08:37:42+02:00 culmination_time=2021-06-09 16:39:49+02:00 set_time=2021-06-09 00:43:41+02:00 aster=<Object type=planet name=Mars />>, <AsterEphemerides rise_time=2021-06-09 01:47:05+02:00 culmination_time=2021-06-09 06:53:50+02:00 set_time=2021-06-09 12:00:35+02:00 aster=<Object type=planet name=Jupiter />>, <AsterEphemerides rise_time=2021-06-09 01:05:33+02:00 culmination_time=2021-06-09 05:40:38+02:00 set_time=2021-06-09 10:15:43+02:00 aster=<Object type=planet name=Saturn />>, <AsterEphemerides rise_time=2021-06-09 03:55:34+02:00 culmination_time=2021-06-09 11:18:01+02:00 set_time=2021-06-09 18:40:29+02:00 aster=<Object type=planet name=Uranus />>, <AsterEphemerides rise_time=2021-06-09 02:26:53+02:00 culmination_time=2021-06-09 08:12:55+02:00 set_time=2021-06-09 13:58:57+02:00 aster=<Object type=planet name=Neptune />>, <AsterEphemerides rise_time=2021-06-09 00:26:08+02:00 culmination_time=2021-06-09 04:31:56+02:00 set_time=2021-06-09 08:37:43+02:00 aster=<Object type=planet name=Pluto />>] | |||
[<AsterEphemerides rise_time=2021-06-09 05:36:00 culmination_time=2021-06-09 13:47:00 set_time=2021-06-09 21:58:00 aster=<Object type=STAR name=SUN />>, <AsterEphemerides rise_time=2021-06-09 04:59:00 culmination_time=2021-06-09 13:02:00 set_time=2021-06-09 21:16:00 aster=<Object type=SATELLITE name=MOON />>, <AsterEphemerides rise_time=2021-06-09 06:06:00 culmination_time=2021-06-09 13:58:00 set_time=2021-06-09 21:49:00 aster=<Object type=PLANET name=MERCURY />>, <AsterEphemerides rise_time=2021-06-09 06:52:00 culmination_time=2021-06-09 15:13:00 set_time=2021-06-09 23:34:00 aster=<Object type=PLANET name=VENUS />>, <AsterEphemerides rise_time=2021-06-09 08:38:00 culmination_time=2021-06-09 16:40:00 set_time=2021-06-09 00:44:00 aster=<Object type=PLANET name=MARS />>, <AsterEphemerides rise_time=2021-06-09 01:47:00 culmination_time=2021-06-09 06:54:00 set_time=2021-06-09 12:01:00 aster=<Object type=PLANET name=JUPITER />>, <AsterEphemerides rise_time=2021-06-09 01:06:00 culmination_time=2021-06-09 05:41:00 set_time=2021-06-09 10:16:00 aster=<Object type=PLANET name=SATURN />>, <AsterEphemerides rise_time=2021-06-09 03:56:00 culmination_time=2021-06-09 11:18:00 set_time=2021-06-09 18:40:00 aster=<Object type=PLANET name=URANUS />>, <AsterEphemerides rise_time=2021-06-09 02:27:00 culmination_time=2021-06-09 08:13:00 set_time=2021-06-09 13:59:00 aster=<Object type=PLANET name=NEPTUNE />>, <AsterEphemerides rise_time=2021-06-09 00:26:00 culmination_time=2021-06-09 04:32:00 set_time=2021-06-09 08:38:00 aster=<Object type=PLANET name=PLUTO />>] | |||
""" | |||
ephemerides = [] | |||
@@ -182,19 +182,25 @@ def get_ephemerides( | |||
# Convert the Time instances to Python datetime objects | |||
if rise_time is not None: | |||
rise_time = translate_to_timezone( | |||
rise_time.utc_datetime().replace(microsecond=0), to_tz=timezone | |||
rise_time = normalize_datetime( | |||
translate_to_timezone( | |||
rise_time.utc_datetime().replace(microsecond=0), to_tz=timezone | |||
) | |||
) | |||
if culmination_time is not None: | |||
culmination_time = translate_to_timezone( | |||
culmination_time.utc_datetime().replace(microsecond=0), | |||
to_tz=timezone, | |||
culmination_time = normalize_datetime( | |||
translate_to_timezone( | |||
culmination_time.utc_datetime().replace(microsecond=0), | |||
to_tz=timezone, | |||
) | |||
) | |||
if set_time is not None: | |||
set_time = translate_to_timezone( | |||
set_time.utc_datetime().replace(microsecond=0), to_tz=timezone | |||
set_time = normalize_datetime( | |||
translate_to_timezone( | |||
set_time.utc_datetime().replace(microsecond=0), to_tz=timezone | |||
) | |||
) | |||
ephemerides.append( | |||
@@ -219,17 +219,17 @@ def get_events(date: date_type = date_type.today(), timezone: int = 0) -> [Event | |||
Find events that happen on April 4th, 2020 (show hours in UTC): | |||
>>> get_events(date_type(2020, 4, 4)) | |||
[<Event type=CONJUNCTION objects=[<Object type=planet name=Mercury />, <Object type=planet name=Neptune />] start=2020-04-04 01:14:39.063308+00:00 end=None details=None />] | |||
[<Event type=CONJUNCTION objects=[<Object type=PLANET name=MERCURY />, <Object type=PLANET name=NEPTUNE />] start=2020-04-04 01:14:39.063308+00:00 end=None details=None />] | |||
Find events that happen on April 4th, 2020 (show timezones in UTC+2): | |||
>>> get_events(date_type(2020, 4, 4), 2) | |||
[<Event type=CONJUNCTION objects=[<Object type=planet name=Mercury />, <Object type=planet name=Neptune />] start=2020-04-04 03:14:39.063267+02:00 end=None details=None />] | |||
[<Event type=CONJUNCTION objects=[<Object type=PLANET name=MERCURY />, <Object type=PLANET name=NEPTUNE />] start=2020-04-04 03:14:39.063267+02:00 end=None details=None />] | |||
Find events that happen on April 3rd, 2020 (show timezones in UTC-2): | |||
>>> get_events(date_type(2020, 4, 3), -2) | |||
[<Event type=CONJUNCTION objects=[<Object type=planet name=Mercury />, <Object type=planet name=Neptune />] start=2020-04-03 23:14:39.063388-02:00 end=None details=None />] | |||
[<Event type=CONJUNCTION objects=[<Object type=PLANET name=MERCURY />, <Object type=PLANET name=NEPTUNE />] start=2020-04-03 23:14:39.063388-02:00 end=None details=None />] | |||
:param date: the date for which the events must be calculated | |||
:param timezone: the timezone to adapt the results to. If not given, defaults to 0. | |||
@@ -10,7 +10,7 @@ from skyfield.api import Topos, Time | |||
from skyfield.vectorlib import VectorSum as SkfPlanet | |||
from .core import get_skf_objects | |||
from .enum import MoonPhaseType, EventType | |||
from .enum import MoonPhaseType, EventType, ObjectIdentifier, ObjectType | |||
class Serializable(ABC): | |||
@@ -66,27 +66,32 @@ class Object(Serializable): | |||
An astronomical object. | |||
""" | |||
def __init__(self, name: str, skyfield_name: str, radius: float = None): | |||
def __init__( | |||
self, identifier: ObjectIdentifier, skyfield_name: str, radius: float = None | |||
): | |||
""" | |||
Initialize an astronomical object | |||
:param str name: the official name of the object (may be internationalized) | |||
:param ObjectIdentifier identifier: the official name of the object (may be internationalized) | |||
:param str skyfield_name: the internal name of the object in Skyfield library | |||
:param float radius: the radius (in km) of the object | |||
:param AsterEphemerides ephemerides: the ephemerides associated to the object | |||
""" | |||
self.name = name | |||
self.identifier = identifier | |||
self.skyfield_name = skyfield_name | |||
self.radius = radius | |||
def __repr__(self): | |||
return "<Object type=%s name=%s />" % (self.get_type(), self.name) | |||
return "<Object type=%s name=%s />" % ( | |||
self.get_type().name, | |||
self.identifier.name, | |||
) | |||
def get_skyfield_object(self) -> SkfPlanet: | |||
return get_skf_objects()[self.skyfield_name] | |||
@abstractmethod | |||
def get_type(self) -> str: | |||
def get_type(self) -> ObjectType: | |||
pass | |||
def get_apparent_radius(self, time: Time, from_place) -> float: | |||
@@ -97,7 +102,7 @@ class Object(Serializable): | |||
:return: | |||
""" | |||
if self.radius is None: | |||
raise ValueError("Missing radius for %s object" % self.name) | |||
raise ValueError("Missing radius for %s" % self.identifier.name) | |||
return ( | |||
360 | |||
@@ -110,30 +115,30 @@ class Object(Serializable): | |||
def serialize(self) -> dict: | |||
return { | |||
"name": self.name, | |||
"identifier": self.identifier.name, | |||
"type": self.get_type(), | |||
"radius": self.radius, | |||
} | |||
class Star(Object): | |||
def get_type(self) -> str: | |||
return "star" | |||
def get_type(self) -> ObjectType: | |||
return ObjectType.STAR | |||
class Planet(Object): | |||
def get_type(self) -> str: | |||
return "planet" | |||
def get_type(self) -> ObjectType: | |||
return ObjectType.PLANET | |||
class DwarfPlanet(Planet): | |||
def get_type(self) -> str: | |||
return "dwarf_planet" | |||
def get_type(self) -> ObjectType: | |||
return ObjectType.DWARF_PLANET | |||
class Satellite(Object): | |||
def get_type(self) -> str: | |||
return "satellite" | |||
def get_type(self) -> ObjectType: | |||
return ObjectType.SATELLITE | |||
class Event(Serializable): | |||
@@ -216,19 +221,19 @@ class AsterEphemerides(Serializable): | |||
} | |||
EARTH = Planet("Earth", "EARTH") | |||
EARTH = Planet(ObjectIdentifier.EARTH, "EARTH") | |||
ASTERS = [ | |||
Star("Sun", "SUN", radius=696342), | |||
Satellite("Moon", "MOON", radius=1737.4), | |||
Planet("Mercury", "MERCURY", radius=2439.7), | |||
Planet("Venus", "VENUS", radius=6051.8), | |||
Planet("Mars", "MARS", radius=3396.2), | |||
Planet("Jupiter", "JUPITER BARYCENTER", radius=71492), | |||
Planet("Saturn", "SATURN BARYCENTER", radius=60268), | |||
Planet("Uranus", "URANUS BARYCENTER", radius=25559), | |||
Planet("Neptune", "NEPTUNE BARYCENTER", radius=24764), | |||
Planet("Pluto", "PLUTO BARYCENTER", radius=1185), | |||
Star(ObjectIdentifier.SUN, "SUN", radius=696342), | |||
Satellite(ObjectIdentifier.MOON, "MOON", radius=1737.4), | |||
Planet(ObjectIdentifier.MERCURY, "MERCURY", radius=2439.7), | |||
Planet(ObjectIdentifier.VENUS, "VENUS", radius=6051.8), | |||
Planet(ObjectIdentifier.MARS, "MARS", radius=3396.2), | |||
Planet(ObjectIdentifier.JUPITER, "JUPITER BARYCENTER", radius=71492), | |||
Planet(ObjectIdentifier.SATURN, "SATURN BARYCENTER", radius=60268), | |||
Planet(ObjectIdentifier.URANUS, "URANUS BARYCENTER", radius=25559), | |||
Planet(ObjectIdentifier.NEPTUNE, "NEPTUNE BARYCENTER", radius=24764), | |||
Planet(ObjectIdentifier.PLUTO, "PLUTO BARYCENTER", radius=1185), | |||
] | |||
@@ -1,18 +1,19 @@ | |||
import unittest | |||
from kosmorrolib import model, core | |||
from kosmorrolib.enum import ObjectIdentifier | |||
class DataTestCase(unittest.TestCase): | |||
def test_object_radius_must_be_set_to_get_apparent_radius(self): | |||
o = model.Planet("Saturn", "SATURN") | |||
o = model.Planet(ObjectIdentifier.SATURN, "SATURN") | |||
with self.assertRaises(ValueError) as context: | |||
o.get_apparent_radius( | |||
core.get_timescale().now(), core.get_skf_objects()["earth"] | |||
) | |||
self.assertEqual(("Missing radius for Saturn object",), context.exception.args) | |||
self.assertEqual(("Missing radius for SATURN",), context.exception.args) | |||
if __name__ == "__main__": | |||
@@ -18,11 +18,11 @@ class EphemeridesTestCase(unittest.TestCase): | |||
def do_assertions(assert_regex): | |||
for ephemeris in eph: | |||
if ephemeris.object.skyfield_name == "SUN": | |||
assert_regex(ephemeris.rise_time.isoformat(), "^2019-11-18T05:41:") | |||
assert_regex(ephemeris.rise_time.isoformat(), "^2019-11-18T05:42:") | |||
assert_regex( | |||
ephemeris.culmination_time.isoformat(), "^2019-11-18T11:45:" | |||
) | |||
assert_regex(ephemeris.set_time.isoformat(), "^2019-11-18T17:48:") | |||
assert_regex(ephemeris.set_time.isoformat(), "^2019-11-18T17:49:") | |||
break | |||
do_assertions() | |||