kerykeion 4.8.1__py3-none-any.whl → 4.12.3__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 kerykeion might be problematic. Click here for more details.
- kerykeion/__init__.py +2 -95
- kerykeion/aspects/aspects_utils.py +0 -12
- kerykeion/aspects/natal_aspects.py +1 -2
- kerykeion/aspects/synastry_aspects.py +3 -4
- kerykeion/astrological_subject.py +316 -123
- kerykeion/charts/charts_utils.py +1 -1
- kerykeion/charts/kerykeion_chart_svg.py +83 -22
- kerykeion/charts/templates/chart.xml +360 -355
- kerykeion/kr_types/chart_types.py +1 -0
- kerykeion/kr_types/kr_literals.py +87 -54
- kerykeion/kr_types/kr_models.py +35 -77
- kerykeion/kr_types/settings_models.py +10 -27
- kerykeion/utilities.py +36 -23
- {kerykeion-4.8.1.dist-info → kerykeion-4.12.3.dist-info}/METADATA +128 -32
- kerykeion-4.12.3.dist-info/RECORD +32 -0
- kerykeion-4.8.1.dist-info/RECORD +0 -32
- {kerykeion-4.8.1.dist-info → kerykeion-4.12.3.dist-info}/LICENSE +0 -0
- {kerykeion-4.8.1.dist-info → kerykeion-4.12.3.dist-info}/WHEEL +0 -0
- {kerykeion-4.8.1.dist-info → kerykeion-4.12.3.dist-info}/entry_points.txt +0 -0
|
@@ -8,6 +8,7 @@ import swisseph as swe
|
|
|
8
8
|
import logging
|
|
9
9
|
|
|
10
10
|
from datetime import datetime
|
|
11
|
+
from functools import cached_property
|
|
11
12
|
from kerykeion.fetch_geonames import FetchGeonames
|
|
12
13
|
from kerykeion.kr_types import (
|
|
13
14
|
KerykeionException,
|
|
@@ -15,6 +16,10 @@ from kerykeion.kr_types import (
|
|
|
15
16
|
AstrologicalSubjectModel,
|
|
16
17
|
LunarPhaseModel,
|
|
17
18
|
KerykeionPointModel,
|
|
19
|
+
PointType,
|
|
20
|
+
SiderealMode,
|
|
21
|
+
HousesSystemIdentifier,
|
|
22
|
+
PerspectiveType
|
|
18
23
|
)
|
|
19
24
|
from kerykeion.utilities import (
|
|
20
25
|
get_number_from_name,
|
|
@@ -22,11 +27,27 @@ from kerykeion.utilities import (
|
|
|
22
27
|
get_planet_house,
|
|
23
28
|
get_moon_emoji_from_phase_int,
|
|
24
29
|
get_moon_phase_name_from_phase_int,
|
|
30
|
+
check_and_adjust_polar_latitude
|
|
25
31
|
)
|
|
26
32
|
from pathlib import Path
|
|
27
|
-
from typing import Union,
|
|
33
|
+
from typing import Union, get_args
|
|
28
34
|
|
|
29
35
|
DEFAULT_GEONAMES_USERNAME = "century.boy"
|
|
36
|
+
DEFAULT_SIDEREAL_MODE = "FAGAN_BRADLEY"
|
|
37
|
+
DEFAULT_HOUSES_SYSTEM_IDENTIFIER = "P"
|
|
38
|
+
DEFAULT_ZODIAC_TYPE = "Tropic"
|
|
39
|
+
DEFAULT_PERSPECTIVE_TYPE = "Apparent Geocentric"
|
|
40
|
+
GEONAMES_DEFAULT_USERNAME_WARNING = (
|
|
41
|
+
"\n********\n"
|
|
42
|
+
"NO GEONAMES USERNAME SET!\n"
|
|
43
|
+
"Using the default geonames username is not recommended, please set a custom one!\n"
|
|
44
|
+
"You can get one for free here:\n"
|
|
45
|
+
"https://www.geonames.org/login\n"
|
|
46
|
+
"Keep in mind that the default username is limited to 2000 requests per hour and is shared with everyone else using this library.\n"
|
|
47
|
+
"********"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
NOW = datetime.now()
|
|
30
51
|
|
|
31
52
|
|
|
32
53
|
class AstrologicalSubject:
|
|
@@ -35,34 +56,41 @@ class AstrologicalSubject:
|
|
|
35
56
|
it's utc and julian day and returns an object with all that data.
|
|
36
57
|
|
|
37
58
|
Args:
|
|
38
|
-
- name (str, optional):
|
|
39
|
-
- year (int, optional):
|
|
40
|
-
- month (int, optional):
|
|
41
|
-
- day (int, optional):
|
|
42
|
-
- hour (int, optional):
|
|
43
|
-
- minute (int, optional):
|
|
59
|
+
- name (str, optional): The name of the subject. Defaults to "Now".
|
|
60
|
+
- year (int, optional): The year of birth. Defaults to the current year.
|
|
61
|
+
- month (int, optional): The month of birth. Defaults to the current month.
|
|
62
|
+
- day (int, optional): The day of birth. Defaults to the current day.
|
|
63
|
+
- hour (int, optional): The hour of birth. Defaults to the current hour.
|
|
64
|
+
- minute (int, optional): Defaults to the current minute.
|
|
44
65
|
- city (str, optional): City or location of birth. Defaults to "London", which is GMT time.
|
|
45
66
|
The city argument is used to get the coordinates and timezone from geonames just in case
|
|
46
67
|
you don't insert them manually (see _get_tz).
|
|
47
68
|
If you insert the coordinates and timezone manually, the city argument is not used for calculations
|
|
48
69
|
but it's still used as a value for the city attribute.
|
|
49
70
|
- nat (str, optional): _ Defaults to "".
|
|
50
|
-
- lng (Union[int, float], optional):
|
|
51
|
-
- lat (Union[int, float], optional):
|
|
52
|
-
- tz_str (Union[str, bool], optional):
|
|
53
|
-
- geonames_username (str, optional):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if you know the UTC datetime but do not have easy access to e.g. timezone identifier
|
|
58
|
-
_ Defaults to None.
|
|
71
|
+
- lng (Union[int, float], optional): Longitude of the birth location. Defaults to 0 (Greenwich, London).
|
|
72
|
+
- lat (Union[int, float], optional): Latitude of the birth location. Defaults to 51.5074 (Greenwich, London).
|
|
73
|
+
- tz_str (Union[str, bool], optional): Timezone of the birth location. Defaults to "GMT".
|
|
74
|
+
- geonames_username (str, optional): The username for the geonames API. Note: Change this to your own username to avoid rate limits!
|
|
75
|
+
You can get one for free here: https://www.geonames.org/login
|
|
76
|
+
- online (bool, optional): Sets if you want to use the online mode, which fetches the timezone and coordinates from geonames.
|
|
77
|
+
If you already have the coordinates and timezone, set this to False. Defaults to True.
|
|
59
78
|
- disable_chiron (bool, optional): Disables the calculation of Chiron. Defaults to False.
|
|
60
79
|
Chiron calculation can create some issues with the Swiss Ephemeris when the date is too far in the past.
|
|
80
|
+
- sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
|
|
81
|
+
The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
|
|
82
|
+
Defaults to "FAGAN_BRADLEY".
|
|
83
|
+
Available modes are visible in the SiderealMode Literal.
|
|
84
|
+
- houses_system_identifier (HousesSystemIdentifier, optional): The system to use for the calculation of the houses.
|
|
85
|
+
Defaults to "P" (Placidus).
|
|
86
|
+
Available systems are visible in the HousesSystemIdentifier Literal.
|
|
87
|
+
- perspective_type (PerspectiveType, optional): The perspective to use for the calculation of the chart.
|
|
88
|
+
Defaults to "Apparent Geocentric".
|
|
89
|
+
Available perspectives are visible in the PerspectiveType Literal.
|
|
61
90
|
"""
|
|
62
91
|
|
|
63
92
|
# Defined by the user
|
|
64
93
|
name: str
|
|
65
|
-
utc_datetime: Union[datetime, None]
|
|
66
94
|
year: int
|
|
67
95
|
month: int
|
|
68
96
|
day: int
|
|
@@ -76,14 +104,17 @@ class AstrologicalSubject:
|
|
|
76
104
|
geonames_username: str
|
|
77
105
|
online: bool
|
|
78
106
|
zodiac_type: ZodiacType
|
|
107
|
+
sidereal_mode: SiderealMode
|
|
108
|
+
houses_system_identifier: HousesSystemIdentifier
|
|
109
|
+
houses_system_name: str
|
|
110
|
+
perspective_type: PerspectiveType
|
|
79
111
|
|
|
80
112
|
# Generated internally
|
|
81
113
|
city_data: dict[str, str]
|
|
82
114
|
julian_day: Union[int, float]
|
|
83
|
-
utc_time: float
|
|
84
|
-
local_time: float
|
|
85
|
-
utc: datetime
|
|
86
115
|
json_dir: Path
|
|
116
|
+
iso_formatted_local_datetime: str
|
|
117
|
+
iso_formatted_utc_datetime: str
|
|
87
118
|
|
|
88
119
|
# Planets
|
|
89
120
|
sun: KerykeionPointModel
|
|
@@ -120,36 +151,29 @@ class AstrologicalSubject:
|
|
|
120
151
|
planets_degrees_ut: list[float]
|
|
121
152
|
houses_degree_ut: list[float]
|
|
122
153
|
|
|
123
|
-
now = datetime.now()
|
|
124
|
-
|
|
125
154
|
def __init__(
|
|
126
155
|
self,
|
|
127
156
|
name="Now",
|
|
128
|
-
year: int =
|
|
129
|
-
month: int =
|
|
130
|
-
day: int =
|
|
131
|
-
hour: int =
|
|
132
|
-
minute: int =
|
|
157
|
+
year: int = NOW.year,
|
|
158
|
+
month: int = NOW.month,
|
|
159
|
+
day: int = NOW.day,
|
|
160
|
+
hour: int = NOW.hour,
|
|
161
|
+
minute: int = NOW.minute,
|
|
133
162
|
city: Union[str, None] = None,
|
|
134
163
|
nation: Union[str, None] = None,
|
|
135
164
|
lng: Union[int, float, None] = None,
|
|
136
165
|
lat: Union[int, float, None] = None,
|
|
137
166
|
tz_str: Union[str, None] = None,
|
|
138
167
|
geonames_username: Union[str, None] = None,
|
|
139
|
-
zodiac_type: ZodiacType =
|
|
168
|
+
zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
|
|
140
169
|
online: bool = True,
|
|
141
|
-
|
|
142
|
-
|
|
170
|
+
disable_chiron: bool = False,
|
|
171
|
+
sidereal_mode: Union[SiderealMode, None] = None,
|
|
172
|
+
houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
|
|
173
|
+
perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE
|
|
143
174
|
) -> None:
|
|
144
175
|
logging.debug("Starting Kerykeion")
|
|
145
176
|
|
|
146
|
-
# We set the swisseph path to the current directory
|
|
147
|
-
swe.set_ephe_path(
|
|
148
|
-
str(
|
|
149
|
-
Path(__file__).parent.absolute() / "sweph"
|
|
150
|
-
)
|
|
151
|
-
)
|
|
152
|
-
|
|
153
177
|
self.name = name
|
|
154
178
|
self.year = year
|
|
155
179
|
self.month = month
|
|
@@ -165,26 +189,19 @@ class AstrologicalSubject:
|
|
|
165
189
|
self.online = online
|
|
166
190
|
self.json_dir = Path.home()
|
|
167
191
|
self.geonames_username = geonames_username
|
|
168
|
-
self.utc_datetime = utc_datetime
|
|
169
192
|
self.disable_chiron = disable_chiron
|
|
193
|
+
self.sidereal_mode = sidereal_mode
|
|
194
|
+
self.houses_system_identifier = houses_system_identifier
|
|
195
|
+
self.perspective_type = perspective_type
|
|
196
|
+
|
|
197
|
+
#---------------#
|
|
198
|
+
# General setup #
|
|
199
|
+
#---------------#
|
|
170
200
|
|
|
171
201
|
# This message is set to encourage the user to set a custom geonames username
|
|
172
202
|
if geonames_username is None and online:
|
|
173
203
|
logging.warning(
|
|
174
|
-
|
|
175
|
-
"********" +
|
|
176
|
-
"\n" +
|
|
177
|
-
"NO GEONAMES USERNAME SET!" +
|
|
178
|
-
"\n" +
|
|
179
|
-
"Using the default geonames username is not recommended, please set a custom one!" +
|
|
180
|
-
"\n" +
|
|
181
|
-
"You can get one for free here:" +
|
|
182
|
-
"\n" +
|
|
183
|
-
"https://www.geonames.org/login" +
|
|
184
|
-
"\n" +
|
|
185
|
-
"Keep in mind that the default username is limited to 2000 requests per hour and is shared with everyone else using this library." +
|
|
186
|
-
"\n" +
|
|
187
|
-
"********"
|
|
204
|
+
|
|
188
205
|
)
|
|
189
206
|
|
|
190
207
|
self.geonames_username = DEFAULT_GEONAMES_USERNAME
|
|
@@ -206,27 +223,101 @@ class AstrologicalSubject:
|
|
|
206
223
|
logging.info("No longitude specified, using London as default")
|
|
207
224
|
|
|
208
225
|
if (not self.online) and (not tz_str):
|
|
209
|
-
raise KerykeionException(
|
|
210
|
-
"You need to set the coordinates and timezone if you want to use the offline mode!"
|
|
211
|
-
)
|
|
226
|
+
raise KerykeionException("You need to set the coordinates and timezone if you want to use the offline mode!")
|
|
212
227
|
|
|
213
|
-
|
|
228
|
+
#-----------------------#
|
|
229
|
+
# Swiss Ephemeris setup #
|
|
230
|
+
#-----------------------#
|
|
231
|
+
|
|
232
|
+
# We set the swisseph path to the current directory
|
|
233
|
+
swe.set_ephe_path(str(Path(__file__).parent.absolute() / "sweph"))
|
|
234
|
+
|
|
235
|
+
# Flags for the Swiss Ephemeris
|
|
236
|
+
self._iflag = swe.FLG_SWIEPH + swe.FLG_SPEED
|
|
237
|
+
|
|
238
|
+
# Chart Perspective check and setup --->
|
|
239
|
+
if self.perspective_type not in get_args(PerspectiveType):
|
|
240
|
+
raise KerykeionException(f"\n* ERROR: '{self.perspective_type}' is NOT a valid chart perspective! Available perspectives are: *" + "\n" + str(get_args(PerspectiveType)))
|
|
241
|
+
|
|
242
|
+
if self.perspective_type == "True Geocentric":
|
|
243
|
+
self._iflag += swe.FLG_TRUEPOS
|
|
244
|
+
elif self.perspective_type == "Heliocentric":
|
|
245
|
+
self._iflag += swe.FLG_HELCTR
|
|
246
|
+
elif self.perspective_type == "Topocentric":
|
|
247
|
+
self._iflag += swe.FLG_TOPOCTR
|
|
248
|
+
# geopos_is_set, for topocentric
|
|
249
|
+
swe.set_topo(self.lng, self.lat, 0)
|
|
250
|
+
# <--- Chart Perspective check and setup
|
|
251
|
+
|
|
252
|
+
# House System check and setup --->
|
|
253
|
+
if self.houses_system_identifier not in get_args(HousesSystemIdentifier):
|
|
254
|
+
raise KerykeionException(f"\n* ERROR: '{self.houses_system_identifier}' is NOT a valid house system! Available systems are: *" + "\n" + str(get_args(HousesSystemIdentifier)))
|
|
255
|
+
|
|
256
|
+
self.houses_system_name = swe.house_name(self.houses_system_identifier.encode('ascii'))
|
|
257
|
+
# <--- House System check and setup
|
|
258
|
+
|
|
259
|
+
# Zodiac Type and Sidereal mode checks and setup --->
|
|
260
|
+
if zodiac_type and not zodiac_type in get_args(ZodiacType):
|
|
261
|
+
raise KerykeionException(f"\n* ERROR: '{zodiac_type}' is NOT a valid zodiac type! Available types are: *" + "\n" + str(get_args(ZodiacType)))
|
|
262
|
+
|
|
263
|
+
if self.sidereal_mode and self.zodiac_type == "Tropic":
|
|
264
|
+
raise KerykeionException("You can't set a sidereal mode with a Tropic zodiac type!")
|
|
265
|
+
|
|
266
|
+
if self.zodiac_type == "Sidereal" and not self.sidereal_mode:
|
|
267
|
+
self.sidereal_mode = DEFAULT_SIDEREAL_MODE
|
|
268
|
+
logging.info("No sidereal mode set, using default FAGAN_BRADLEY")
|
|
269
|
+
|
|
270
|
+
if self.zodiac_type == "Sidereal":
|
|
271
|
+
# Check if the sidereal mode is valid
|
|
272
|
+
if not self.sidereal_mode in get_args(SiderealMode):
|
|
273
|
+
raise KerykeionException(f"\n* ERROR: '{self.sidereal_mode}' is NOT a valid sidereal mode! Available modes are: *" + "\n" + str(get_args(SiderealMode)))
|
|
274
|
+
|
|
275
|
+
self._iflag += swe.FLG_SIDEREAL
|
|
276
|
+
mode = "SIDM_" + self.sidereal_mode
|
|
277
|
+
swe.set_sid_mode(getattr(swe, mode))
|
|
278
|
+
logging.debug(f"Using sidereal mode: {mode}")
|
|
279
|
+
# <--- Zodiac Type and Sidereal mode checks and setup
|
|
280
|
+
|
|
281
|
+
#------------------------#
|
|
282
|
+
# Start the calculations #
|
|
283
|
+
#------------------------#
|
|
284
|
+
|
|
285
|
+
check_and_adjust_polar_latitude(self.lat, self.lng)
|
|
286
|
+
|
|
287
|
+
# UTC, julian day and local time setup --->
|
|
288
|
+
if (self.online) and (not self.tz_str):
|
|
289
|
+
self._fetch_tz_from_geonames()
|
|
290
|
+
|
|
291
|
+
# Local time to UTC
|
|
292
|
+
local_time = pytz.timezone(self.tz_str)
|
|
293
|
+
naive_datetime = datetime(self.year, self.month, self.day, self.hour, self.minute, 0)
|
|
294
|
+
local_datetime = local_time.localize(naive_datetime, is_dst=None)
|
|
295
|
+
utc_object = local_datetime.astimezone(pytz.utc)
|
|
296
|
+
self.iso_formatted_utc_datetime = utc_object.isoformat()
|
|
297
|
+
|
|
298
|
+
# ISO formatted local datetime
|
|
299
|
+
self.iso_formatted_local_datetime = local_datetime.isoformat()
|
|
300
|
+
|
|
301
|
+
# Julian day calculation
|
|
302
|
+
utc_float_hour_with_minutes = utc_object.hour + (utc_object.minute / 60)
|
|
303
|
+
self.julian_day = float(swe.julday(utc_object.year, utc_object.month, utc_object.day, utc_float_hour_with_minutes))
|
|
304
|
+
# <--- UTC, julian day and local time setup
|
|
214
305
|
|
|
215
|
-
# Initialize everything
|
|
216
|
-
self._get_utc()
|
|
217
|
-
self._get_jd()
|
|
218
306
|
self._planets_degrees_lister()
|
|
219
307
|
self._planets()
|
|
220
308
|
self._houses()
|
|
221
|
-
|
|
222
309
|
self._planets_in_houses()
|
|
223
310
|
self._lunar_phase_calc()
|
|
224
311
|
|
|
312
|
+
# Deprecated properties
|
|
313
|
+
self.utc_time
|
|
314
|
+
self.local_time
|
|
315
|
+
|
|
225
316
|
def __str__(self) -> str:
|
|
226
|
-
return f"Astrological data for: {self.name}, {self.
|
|
317
|
+
return f"Astrological data for: {self.name}, {self.iso_formatted_utc_datetime} UTC\nBirth location: {self.city}, Lat {self.lat}, Lon {self.lng}"
|
|
227
318
|
|
|
228
319
|
def __repr__(self) -> str:
|
|
229
|
-
return f"Astrological data for: {self.name}, {self.
|
|
320
|
+
return f"Astrological data for: {self.name}, {self.iso_formatted_utc_datetime} UTC\nBirth location: {self.city}, Lat {self.lat}, Lon {self.lng}"
|
|
230
321
|
|
|
231
322
|
def __getitem__(self, item):
|
|
232
323
|
return getattr(self, item)
|
|
@@ -258,32 +349,7 @@ class AstrologicalSubject:
|
|
|
258
349
|
self.lat = float(self.city_data["lat"])
|
|
259
350
|
self.tz_str = self.city_data["timezonestr"]
|
|
260
351
|
|
|
261
|
-
self.
|
|
262
|
-
|
|
263
|
-
def _get_utc(self) -> None:
|
|
264
|
-
"""Converts local time to utc time."""
|
|
265
|
-
|
|
266
|
-
# If the coordinates are not set, get them from geonames.
|
|
267
|
-
if (self.online) and (not self.tz_str):
|
|
268
|
-
self._fetch_tz_from_geonames()
|
|
269
|
-
|
|
270
|
-
# If UTC datetime is provided, then use it directly
|
|
271
|
-
if (self.utc_datetime):
|
|
272
|
-
self.utc = self.utc_datetime
|
|
273
|
-
return
|
|
274
|
-
|
|
275
|
-
local_time = pytz.timezone(self.tz_str)
|
|
276
|
-
|
|
277
|
-
naive_datetime = datetime(self.year, self.month, self.day, self.hour, self.minute, 0)
|
|
278
|
-
|
|
279
|
-
local_datetime = local_time.localize(naive_datetime, is_dst=None)
|
|
280
|
-
self.utc = local_datetime.astimezone(pytz.utc)
|
|
281
|
-
|
|
282
|
-
def _get_jd(self) -> None:
|
|
283
|
-
"""Calculates julian day from the utc time."""
|
|
284
|
-
self.utc_time = self.utc.hour + self.utc.minute / 60
|
|
285
|
-
self.local_time = self.hour + self.minute / 60
|
|
286
|
-
self.julian_day = float(swe.julday(self.utc.year, self.utc.month, self.utc.day, self.utc_time))
|
|
352
|
+
check_and_adjust_polar_latitude(self.lat, self.lng)
|
|
287
353
|
|
|
288
354
|
def _houses(self) -> None:
|
|
289
355
|
"""
|
|
@@ -321,18 +387,21 @@ class AstrologicalSubject:
|
|
|
321
387
|
|
|
322
388
|
if self.zodiac_type == "Sidereal":
|
|
323
389
|
self.houses_degree_ut = swe.houses_ex(
|
|
324
|
-
tjdut=self.julian_day,
|
|
390
|
+
tjdut=self.julian_day,
|
|
391
|
+
lat=self.lat, lon=self.lng,
|
|
392
|
+
hsys=str.encode(self.houses_system_identifier),
|
|
393
|
+
flags=swe.FLG_SIDEREAL
|
|
325
394
|
)[0]
|
|
395
|
+
|
|
326
396
|
elif self.zodiac_type == "Tropic":
|
|
327
397
|
self.houses_degree_ut = swe.houses(
|
|
328
|
-
tjdut=self.julian_day, lat=self.lat,
|
|
398
|
+
tjdut=self.julian_day, lat=self.lat,
|
|
399
|
+
lon=self.lng,
|
|
400
|
+
hsys=str.encode(self.houses_system_identifier)
|
|
329
401
|
)[0]
|
|
330
|
-
else:
|
|
331
|
-
raise KerykeionException("Zodiac type not recognized! Please use 'Tropic' or 'Sidereal'")
|
|
332
402
|
|
|
333
|
-
point_type:
|
|
334
|
-
|
|
335
|
-
self.houses_degree_ut = swe.houses(self.julian_day, self.lat, self.lng)[0]
|
|
403
|
+
point_type: PointType = "House"
|
|
404
|
+
|
|
336
405
|
# stores the house in singular dictionaries.
|
|
337
406
|
self.first_house = calculate_position(self.houses_degree_ut[0], "First_House", point_type=point_type)
|
|
338
407
|
self.second_house = calculate_position(self.houses_degree_ut[1], "Second_House", point_type=point_type)
|
|
@@ -364,12 +433,6 @@ class AstrologicalSubject:
|
|
|
364
433
|
|
|
365
434
|
def _planets_degrees_lister(self):
|
|
366
435
|
"""Sidereal or tropic mode."""
|
|
367
|
-
self._iflag = swe.FLG_SWIEPH + swe.FLG_SPEED
|
|
368
|
-
|
|
369
|
-
if self.zodiac_type == "Sidereal":
|
|
370
|
-
self._iflag += swe.FLG_SIDEREAL
|
|
371
|
-
mode = "SIDM_FAGAN_BRADLEY"
|
|
372
|
-
swe.set_sid_mode(getattr(swe, mode))
|
|
373
436
|
|
|
374
437
|
# Calculates the position of the planets and stores it in a list.
|
|
375
438
|
sun_deg = swe.calc(self.julian_day, 0, self._iflag)[0][0]
|
|
@@ -412,7 +475,7 @@ class AstrologicalSubject:
|
|
|
412
475
|
"""Defines body positon in signs and information and
|
|
413
476
|
stores them in dictionaries"""
|
|
414
477
|
|
|
415
|
-
point_type:
|
|
478
|
+
point_type: PointType = "Planet"
|
|
416
479
|
# stores the planets in singular dictionaries.
|
|
417
480
|
self.sun = calculate_position(self.planets_degrees_ut[0], "Sun", point_type=point_type)
|
|
418
481
|
self.moon = calculate_position(self.planets_degrees_ut[1], "Moon", point_type=point_type)
|
|
@@ -474,13 +537,13 @@ class AstrologicalSubject:
|
|
|
474
537
|
|
|
475
538
|
# Check in retrograde or not:
|
|
476
539
|
planets_ret = []
|
|
477
|
-
for
|
|
478
|
-
planet_number = get_number_from_name(
|
|
540
|
+
for planet in self.planets_list:
|
|
541
|
+
planet_number = get_number_from_name(planet["name"])
|
|
479
542
|
if swe.calc(self.julian_day, planet_number, self._iflag)[0][3] < 0:
|
|
480
|
-
|
|
543
|
+
planet["retrograde"] = True
|
|
481
544
|
else:
|
|
482
|
-
|
|
483
|
-
planets_ret.append(
|
|
545
|
+
planet["retrograde"] = False
|
|
546
|
+
planets_ret.append(planet)
|
|
484
547
|
|
|
485
548
|
def _lunar_phase_calc(self) -> None:
|
|
486
549
|
"""Function to calculate the lunar phase"""
|
|
@@ -555,20 +618,7 @@ class AstrologicalSubject:
|
|
|
555
618
|
|
|
556
619
|
self.lunar_phase = LunarPhaseModel(**lunar_phase_dictionary)
|
|
557
620
|
|
|
558
|
-
def
|
|
559
|
-
"""
|
|
560
|
-
Utility function to check if the location is in the polar circle.
|
|
561
|
-
If it is, it sets the latitude to 66 or -66 degrees.
|
|
562
|
-
"""
|
|
563
|
-
if self.lat > 66.0:
|
|
564
|
-
self.lat = 66.0
|
|
565
|
-
logging.info("Polar circle override for houses, using 66 degrees")
|
|
566
|
-
|
|
567
|
-
elif self.lat < -66.0:
|
|
568
|
-
self.lat = -66.0
|
|
569
|
-
logging.info("Polar circle override for houses, using -66 degrees")
|
|
570
|
-
|
|
571
|
-
def json(self, dump=False, destination_folder: Union[str, None] = None) -> str:
|
|
621
|
+
def json(self, dump=False, destination_folder: Union[str, None] = None, indent: Union[int, None] = None) -> str:
|
|
572
622
|
"""
|
|
573
623
|
Dumps the Kerykeion object to a json string foramt,
|
|
574
624
|
if dump=True also dumps to file located in destination
|
|
@@ -576,8 +626,7 @@ class AstrologicalSubject:
|
|
|
576
626
|
"""
|
|
577
627
|
|
|
578
628
|
KrData = AstrologicalSubjectModel(**self.__dict__)
|
|
579
|
-
json_string = KrData.model_dump_json(exclude_none=True)
|
|
580
|
-
print(json_string)
|
|
629
|
+
json_string = KrData.model_dump_json(exclude_none=True, indent=indent)
|
|
581
630
|
|
|
582
631
|
if dump:
|
|
583
632
|
if destination_folder:
|
|
@@ -600,6 +649,131 @@ class AstrologicalSubject:
|
|
|
600
649
|
|
|
601
650
|
return AstrologicalSubjectModel(**self.__dict__)
|
|
602
651
|
|
|
652
|
+
@cached_property
|
|
653
|
+
def utc_time(self) -> float:
|
|
654
|
+
"""
|
|
655
|
+
Deprecated property, use iso_formatted_utc_datetime instead, will be removed in the future.
|
|
656
|
+
Returns the UTC time as a float.
|
|
657
|
+
"""
|
|
658
|
+
dt = datetime.fromisoformat(self.iso_formatted_utc_datetime)
|
|
659
|
+
|
|
660
|
+
# Extract the hours, minutes, and seconds
|
|
661
|
+
hours = dt.hour
|
|
662
|
+
minutes = dt.minute
|
|
663
|
+
seconds = dt.second + dt.microsecond / 1_000_000
|
|
664
|
+
|
|
665
|
+
# Convert time to float hours
|
|
666
|
+
float_time = hours + minutes / 60 + seconds / 3600
|
|
667
|
+
|
|
668
|
+
return float_time
|
|
669
|
+
|
|
670
|
+
@cached_property
|
|
671
|
+
def local_time(self) -> float:
|
|
672
|
+
"""
|
|
673
|
+
Deprecated property, use iso_formatted_local_datetime instead, will be removed in the future.
|
|
674
|
+
Returns the local time as a float.
|
|
675
|
+
"""
|
|
676
|
+
dt = datetime.fromisoformat(self.iso_formatted_local_datetime)
|
|
677
|
+
|
|
678
|
+
# Extract the hours, minutes, and seconds
|
|
679
|
+
hours = dt.hour
|
|
680
|
+
minutes = dt.minute
|
|
681
|
+
seconds = dt.second + dt.microsecond / 1_000_000
|
|
682
|
+
|
|
683
|
+
# Convert time to float hours
|
|
684
|
+
float_time = hours + minutes / 60 + seconds / 3600
|
|
685
|
+
|
|
686
|
+
return float_time
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
@staticmethod
|
|
690
|
+
def get_from_iso_utc_time(
|
|
691
|
+
name: str,
|
|
692
|
+
iso_utc_time: str,
|
|
693
|
+
city: str = "Greenwich",
|
|
694
|
+
nation: str = "GB",
|
|
695
|
+
tz_str: str = "Etc/GMT",
|
|
696
|
+
online: bool = False,
|
|
697
|
+
lng: Union[int, float] = 0,
|
|
698
|
+
lat: Union[int, float] = 51.5074,
|
|
699
|
+
geonames_username: str = DEFAULT_GEONAMES_USERNAME,
|
|
700
|
+
zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
|
|
701
|
+
disable_chiron: bool = False,
|
|
702
|
+
sidereal_mode: Union[SiderealMode, None] = None,
|
|
703
|
+
houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
|
|
704
|
+
perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE
|
|
705
|
+
|
|
706
|
+
) -> "AstrologicalSubject":
|
|
707
|
+
"""
|
|
708
|
+
Creates an AstrologicalSubject object from an iso formatted UTC time.
|
|
709
|
+
This method is offline by default, set online=True to fetch the timezone and coordinates from geonames.
|
|
710
|
+
|
|
711
|
+
Args:
|
|
712
|
+
- name (str): The name of the subject.
|
|
713
|
+
- iso_utc_time (str): The iso formatted UTC time.
|
|
714
|
+
- city (str, optional): City or location of birth. Defaults to "Greenwich".
|
|
715
|
+
- nation (str, optional): Nation of birth. Defaults to "GB".
|
|
716
|
+
- tz_str (str, optional): Timezone of the birth location. Defaults to "Etc/GMT".
|
|
717
|
+
- online (bool, optional): Sets if you want to use the online mode, which fetches the timezone and coordinates from geonames.
|
|
718
|
+
If you already have the coordinates and timezone, set this to False. Defaults to False.
|
|
719
|
+
- lng (Union[int, float], optional): Longitude of the birth location. Defaults to 0 (Greenwich, London).
|
|
720
|
+
- lat (Union[int, float], optional): Latitude of the birth location. Defaults to 51.5074 (Greenwich, London).
|
|
721
|
+
- geonames_username (str, optional): The username for the geonames API. Note: Change this to your own username to avoid rate limits!
|
|
722
|
+
You can get one for free here: https://www.geonames.org/login
|
|
723
|
+
- zodiac_type (ZodiacType, optional): The zodiac type to use. Defaults to "Tropic".
|
|
724
|
+
- disable_chiron (bool, optional): Disables the calculation of Chiron. Defaults to False.
|
|
725
|
+
Chiron calculation can create some issues with the Swiss Ephemeris when the date is too far in the past.
|
|
726
|
+
- sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
|
|
727
|
+
The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
|
|
728
|
+
Defaults to None.
|
|
729
|
+
Available modes are visible in the SiderealMode Literal.
|
|
730
|
+
- houses_system_identifier (HousesSystemIdentifier, optional): The system to use for the calculation of the houses.
|
|
731
|
+
Defaults to "P" (Placidus).
|
|
732
|
+
Available systems are visible in the HousesSystemIdentifier Literal.
|
|
733
|
+
- perspective_type (PerspectiveType, optional): The perspective to use for the calculation of the chart.
|
|
734
|
+
Defaults to "Apparent Geocentric".
|
|
735
|
+
|
|
736
|
+
Returns:
|
|
737
|
+
- AstrologicalSubject: The AstrologicalSubject object.
|
|
738
|
+
"""
|
|
739
|
+
dt = datetime.fromisoformat(iso_utc_time)
|
|
740
|
+
|
|
741
|
+
if online == True:
|
|
742
|
+
if geonames_username == DEFAULT_GEONAMES_USERNAME:
|
|
743
|
+
logging.warning(GEONAMES_DEFAULT_USERNAME_WARNING)
|
|
744
|
+
|
|
745
|
+
geonames = FetchGeonames(
|
|
746
|
+
city,
|
|
747
|
+
nation,
|
|
748
|
+
username=geonames_username,
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
city_data: dict[str, str] = geonames.get_serialized_data()
|
|
752
|
+
lng = float(city_data["lng"])
|
|
753
|
+
lat = float(city_data["lat"])
|
|
754
|
+
|
|
755
|
+
subject = AstrologicalSubject(
|
|
756
|
+
name=name,
|
|
757
|
+
year=dt.year,
|
|
758
|
+
month=dt.month,
|
|
759
|
+
day=dt.day,
|
|
760
|
+
hour=dt.hour,
|
|
761
|
+
minute=dt.minute,
|
|
762
|
+
city=city,
|
|
763
|
+
nation=city,
|
|
764
|
+
lng=lng,
|
|
765
|
+
lat=lat,
|
|
766
|
+
tz_str=tz_str,
|
|
767
|
+
online=False,
|
|
768
|
+
geonames_username=geonames_username,
|
|
769
|
+
zodiac_type=zodiac_type,
|
|
770
|
+
disable_chiron=disable_chiron,
|
|
771
|
+
sidereal_mode=sidereal_mode,
|
|
772
|
+
houses_system_identifier=houses_system_identifier,
|
|
773
|
+
perspective_type=perspective_type
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
return subject
|
|
603
777
|
|
|
604
778
|
if __name__ == "__main__":
|
|
605
779
|
import json
|
|
@@ -620,3 +794,22 @@ if __name__ == "__main__":
|
|
|
620
794
|
|
|
621
795
|
print('\n')
|
|
622
796
|
print(johnny.chiron)
|
|
797
|
+
|
|
798
|
+
# With Sidereal Zodiac
|
|
799
|
+
johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", zodiac_type="Sidereal", sidereal_mode="LAHIRI")
|
|
800
|
+
print(johnny.json(dump=True, indent=2))
|
|
801
|
+
|
|
802
|
+
# With Morinus Houses
|
|
803
|
+
johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", houses_system_identifier="M")
|
|
804
|
+
print(johnny.json(dump=True, indent=2))
|
|
805
|
+
|
|
806
|
+
# With True Geocentric Perspective
|
|
807
|
+
johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", perspective_type="True Geocentric")
|
|
808
|
+
print(johnny.json(dump=True, indent=2))
|
|
809
|
+
|
|
810
|
+
# With Heliocentric Perspective
|
|
811
|
+
johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", perspective_type="Heliocentric")
|
|
812
|
+
print(johnny.json(dump=True, indent=2))
|
|
813
|
+
|
|
814
|
+
# With Topocentric Perspective
|
|
815
|
+
johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", perspective_type="Topocentric")
|
kerykeion/charts/charts_utils.py
CHANGED
|
@@ -126,7 +126,7 @@ def draw_zodiac_slice(
|
|
|
126
126
|
|
|
127
127
|
Args:
|
|
128
128
|
- c1 (Union[int, float]): The value of c1.
|
|
129
|
-
- chart_type (
|
|
129
|
+
- chart_type (ChartType): The type of chart.
|
|
130
130
|
- seventh_house_degree_ut (Union[int, float]): The degree of the seventh house.
|
|
131
131
|
- num (int): The number of the sign. Note: In OpenAstro it did refer to self.zodiac,
|
|
132
132
|
which is a list of the signs in order, starting with Aries. Eg:
|