kerykeion 4.26.2__py3-none-any.whl → 5.0.0__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.

Files changed (77) hide show
  1. kerykeion/__init__.py +54 -11
  2. kerykeion/aspects/__init__.py +5 -2
  3. kerykeion/aspects/aspects_factory.py +569 -0
  4. kerykeion/aspects/aspects_utils.py +81 -8
  5. kerykeion/astrological_subject_factory.py +1897 -0
  6. kerykeion/backword.py +773 -0
  7. kerykeion/chart_data_factory.py +549 -0
  8. kerykeion/charts/chart_drawer.py +2601 -0
  9. kerykeion/charts/charts_utils.py +948 -177
  10. kerykeion/charts/draw_planets.py +602 -351
  11. kerykeion/charts/templates/aspect_grid_only.xml +328 -202
  12. kerykeion/charts/templates/chart.xml +432 -272
  13. kerykeion/charts/templates/wheel_only.xml +350 -214
  14. kerykeion/charts/themes/black-and-white.css +148 -0
  15. kerykeion/charts/themes/classic.css +107 -76
  16. kerykeion/charts/themes/dark-high-contrast.css +145 -107
  17. kerykeion/charts/themes/dark.css +146 -107
  18. kerykeion/charts/themes/light.css +146 -103
  19. kerykeion/charts/themes/strawberry.css +158 -0
  20. kerykeion/composite_subject_factory.py +253 -51
  21. kerykeion/ephemeris_data_factory.py +434 -0
  22. kerykeion/fetch_geonames.py +27 -8
  23. kerykeion/house_comparison/__init__.py +6 -0
  24. kerykeion/house_comparison/house_comparison_factory.py +103 -0
  25. kerykeion/house_comparison/house_comparison_utils.py +126 -0
  26. kerykeion/kr_types/__init__.py +66 -6
  27. kerykeion/kr_types/chart_template_model.py +20 -0
  28. kerykeion/kr_types/kerykeion_exception.py +15 -9
  29. kerykeion/kr_types/kr_literals.py +14 -132
  30. kerykeion/kr_types/kr_models.py +14 -318
  31. kerykeion/kr_types/settings_models.py +15 -203
  32. kerykeion/planetary_return_factory.py +805 -0
  33. kerykeion/relationship_score_factory.py +301 -0
  34. kerykeion/report.py +751 -64
  35. kerykeion/schemas/__init__.py +106 -0
  36. kerykeion/schemas/chart_template_model.py +367 -0
  37. kerykeion/schemas/kerykeion_exception.py +20 -0
  38. kerykeion/schemas/kr_literals.py +181 -0
  39. kerykeion/schemas/kr_models.py +605 -0
  40. kerykeion/schemas/settings_models.py +180 -0
  41. kerykeion/settings/__init__.py +20 -1
  42. kerykeion/settings/chart_defaults.py +444 -0
  43. kerykeion/settings/config_constants.py +117 -12
  44. kerykeion/settings/kerykeion_settings.py +31 -73
  45. kerykeion/settings/translation_strings.py +1479 -0
  46. kerykeion/settings/translations.py +74 -0
  47. kerykeion/sweph/ast136/s136108s.se1 +0 -0
  48. kerykeion/sweph/ast136/s136199s.se1 +0 -0
  49. kerykeion/sweph/ast136/s136472s.se1 +0 -0
  50. kerykeion/sweph/ast28/se28978s.se1 +0 -0
  51. kerykeion/sweph/ast50/se50000s.se1 +0 -0
  52. kerykeion/sweph/ast90/se90377s.se1 +0 -0
  53. kerykeion/sweph/ast90/se90482s.se1 +0 -0
  54. kerykeion/sweph/sefstars.txt +1602 -0
  55. kerykeion/transits_time_range_factory.py +302 -0
  56. kerykeion/utilities.py +393 -114
  57. kerykeion-5.0.0.dist-info/METADATA +1176 -0
  58. kerykeion-5.0.0.dist-info/RECORD +63 -0
  59. {kerykeion-4.26.2.dist-info → kerykeion-5.0.0.dist-info}/WHEEL +1 -1
  60. kerykeion/aspects/natal_aspects.py +0 -172
  61. kerykeion/aspects/synastry_aspects.py +0 -124
  62. kerykeion/aspects/transits_time_range.py +0 -41
  63. kerykeion/astrological_subject.py +0 -841
  64. kerykeion/charts/kerykeion_chart_svg.py +0 -1219
  65. kerykeion/enums.py +0 -57
  66. kerykeion/ephemeris_data.py +0 -242
  67. kerykeion/kr_types/chart_types.py +0 -95
  68. kerykeion/relationship_score/__init__.py +0 -2
  69. kerykeion/relationship_score/relationship_score.py +0 -175
  70. kerykeion/relationship_score/relationship_score_factory.py +0 -230
  71. kerykeion/settings/kr.config.json +0 -1258
  72. kerykeion/transits_time_range.py +0 -124
  73. kerykeion-4.26.2.dist-info/LICENSE +0 -661
  74. kerykeion-4.26.2.dist-info/METADATA +0 -629
  75. kerykeion-4.26.2.dist-info/RECORD +0 -46
  76. kerykeion-4.26.2.dist-info/entry_points.txt +0 -3
  77. /LICENSE → /kerykeion-5.0.0.dist-info/licenses/LICENSE +0 -0
@@ -1,841 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- This is part of Kerykeion (C) 2025 Giacomo Battaglia
4
- """
5
-
6
- import pytz
7
- import swisseph as swe
8
- import logging
9
- import warnings
10
-
11
- import math
12
- from datetime import datetime
13
-
14
- from functools import cached_property
15
- from kerykeion.fetch_geonames import FetchGeonames
16
- from kerykeion.kr_types import (
17
- KerykeionException,
18
- ZodiacType,
19
- AstrologicalSubjectModel,
20
- LunarPhaseModel,
21
- KerykeionPointModel,
22
- PointType,
23
- SiderealMode,
24
- HousesSystemIdentifier,
25
- PerspectiveType,
26
- Planet,
27
- Houses,
28
- AxialCusps,
29
- )
30
- from kerykeion.utilities import (
31
- get_number_from_name,
32
- get_kerykeion_point_from_degree,
33
- get_planet_house,
34
- check_and_adjust_polar_latitude,
35
- calculate_moon_phase
36
- )
37
- from pathlib import Path
38
- from typing import Union, get_args
39
-
40
- DEFAULT_GEONAMES_USERNAME = "century.boy"
41
- DEFAULT_SIDEREAL_MODE: SiderealMode = "FAGAN_BRADLEY"
42
- DEFAULT_HOUSES_SYSTEM_IDENTIFIER: HousesSystemIdentifier = "P"
43
- DEFAULT_ZODIAC_TYPE: ZodiacType = "Tropic"
44
- DEFAULT_PERSPECTIVE_TYPE: PerspectiveType = "Apparent Geocentric"
45
- DEFAULT_GEONAMES_CACHE_EXPIRE_AFTER_DAYS = 30
46
- GEONAMES_DEFAULT_USERNAME_WARNING = (
47
- "\n********\n"
48
- "NO GEONAMES USERNAME SET!\n"
49
- "Using the default geonames username is not recommended, please set a custom one!\n"
50
- "You can get one for free here:\n"
51
- "https://www.geonames.org/login\n"
52
- "Keep in mind that the default username is limited to 2000 requests per hour and is shared with everyone else using this library.\n"
53
- "********"
54
- )
55
-
56
- NOW = datetime.now()
57
-
58
-
59
- class AstrologicalSubject:
60
- """
61
- Calculates all the astrological information, the coordinates,
62
- it's utc and julian day and returns an object with all that data.
63
-
64
- Args:
65
- - name (str, optional): The name of the subject. Defaults to "Now".
66
- - year (int, optional): The year of birth. Defaults to the current year.
67
- - month (int, optional): The month of birth. Defaults to the current month.
68
- - day (int, optional): The day of birth. Defaults to the current day.
69
- - hour (int, optional): The hour of birth. Defaults to the current hour.
70
- - minute (int, optional): Defaults to the current minute.
71
- - city (str, optional): City or location of birth. Defaults to "London", which is GMT time.
72
- The city argument is used to get the coordinates and timezone from geonames just in case
73
- you don't insert them manually (see _get_tz).
74
- If you insert the coordinates and timezone manually, the city argument is not used for calculations
75
- but it's still used as a value for the city attribute.
76
- - nat (str, optional): _ Defaults to "".
77
- - lng (Union[int, float], optional): Longitude of the birth location. Defaults to 0 (Greenwich, London).
78
- - lat (Union[int, float], optional): Latitude of the birth location. Defaults to 51.5074 (Greenwich, London).
79
- - tz_str (Union[str, bool], optional): Timezone of the birth location. Defaults to "GMT".
80
- - geonames_username (str, optional): The username for the geonames API. Note: Change this to your own username to avoid rate limits!
81
- You can get one for free here: https://www.geonames.org/login
82
- - online (bool, optional): Sets if you want to use the online mode, which fetches the timezone and coordinates from geonames.
83
- If you already have the coordinates and timezone, set this to False. Defaults to True.
84
- - disable_chiron: Deprecated, use disable_chiron_and_lilith instead.
85
- - sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
86
- The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
87
- Defaults to "FAGAN_BRADLEY".
88
- Available modes are visible in the SiderealMode Literal.
89
- - houses_system_identifier (HousesSystemIdentifier, optional): The system to use for the calculation of the houses.
90
- Defaults to "P" (Placidus).
91
- Available systems are visible in the HousesSystemIdentifier Literal.
92
- - perspective_type (PerspectiveType, optional): The perspective to use for the calculation of the chart.
93
- Defaults to "Apparent Geocentric".
94
- Available perspectives are visible in the PerspectiveType Literal.
95
- - cache_expire_after_days (int, optional): The number of days after which the geonames cache will expire. Defaults to 30.
96
- - is_dst (Union[None, bool], optional): Specify if the time is in DST. Defaults to None.
97
- By default (None), the library will try to guess if the time is in DST or not and raise an AmbiguousTimeError
98
- if it can't guess. If you know the time is in DST, set this to True, if you know it's not, set it to False.
99
- - disable_chiron_and_lilith (bool, optional): boolean representing if Chiron and Lilith should be disabled. Default is False.
100
- Chiron calculation can create some issues with the Swiss Ephemeris when the date is too far in the past.
101
- """
102
-
103
- # Defined by the user
104
- name: str
105
- year: int
106
- month: int
107
- day: int
108
- hour: int
109
- minute: int
110
- city: str
111
- nation: str
112
- lng: Union[int, float]
113
- lat: Union[int, float]
114
- tz_str: str
115
- geonames_username: str
116
- online: bool
117
- zodiac_type: ZodiacType
118
- sidereal_mode: Union[SiderealMode, None]
119
- houses_system_identifier: HousesSystemIdentifier
120
- houses_system_name: str
121
- perspective_type: PerspectiveType
122
- is_dst: Union[None, bool]
123
-
124
- # Generated internally
125
- city_data: dict[str, str]
126
- julian_day: Union[int, float]
127
- json_dir: Path
128
- iso_formatted_local_datetime: str
129
- iso_formatted_utc_datetime: str
130
-
131
- # Planets
132
- sun: KerykeionPointModel
133
- moon: KerykeionPointModel
134
- mercury: KerykeionPointModel
135
- venus: KerykeionPointModel
136
- mars: KerykeionPointModel
137
- jupiter: KerykeionPointModel
138
- saturn: KerykeionPointModel
139
- uranus: KerykeionPointModel
140
- neptune: KerykeionPointModel
141
- pluto: KerykeionPointModel
142
- true_node: KerykeionPointModel
143
- mean_node: KerykeionPointModel
144
- chiron: Union[KerykeionPointModel, None]
145
- mean_lilith: Union[KerykeionPointModel, None]
146
- true_south_node: KerykeionPointModel
147
- mean_south_node: KerykeionPointModel
148
-
149
- # Axes
150
- asc: KerykeionPointModel
151
- dsc: KerykeionPointModel
152
- mc: KerykeionPointModel
153
- ic: KerykeionPointModel
154
-
155
- # Houses
156
- first_house: KerykeionPointModel
157
- second_house: KerykeionPointModel
158
- third_house: KerykeionPointModel
159
- fourth_house: KerykeionPointModel
160
- fifth_house: KerykeionPointModel
161
- sixth_house: KerykeionPointModel
162
- seventh_house: KerykeionPointModel
163
- eighth_house: KerykeionPointModel
164
- ninth_house: KerykeionPointModel
165
- tenth_house: KerykeionPointModel
166
- eleventh_house: KerykeionPointModel
167
- twelfth_house: KerykeionPointModel
168
-
169
- # Lists
170
- _houses_list: list[KerykeionPointModel]
171
- _houses_degree_ut: list[float]
172
- planets_names_list: list[Planet]
173
- houses_names_list: list[Houses]
174
- axial_cusps_names_list: list[AxialCusps]
175
-
176
- # Enable or disable features
177
- disable_chiron: Union[None, bool]
178
- disable_chiron_and_lilith: bool
179
-
180
- lunar_phase: LunarPhaseModel
181
-
182
- def __init__(
183
- self,
184
- name="Now",
185
- year: int = NOW.year,
186
- month: int = NOW.month,
187
- day: int = NOW.day,
188
- hour: int = NOW.hour,
189
- minute: int = NOW.minute,
190
- city: Union[str, None] = None,
191
- nation: Union[str, None] = None,
192
- lng: Union[int, float, None] = None,
193
- lat: Union[int, float, None] = None,
194
- tz_str: Union[str, None] = None,
195
- geonames_username: Union[str, None] = None,
196
- zodiac_type: Union[ZodiacType, None] = DEFAULT_ZODIAC_TYPE,
197
- online: bool = True,
198
- disable_chiron: Union[None, bool] = None, # Deprecated
199
- sidereal_mode: Union[SiderealMode, None] = None,
200
- houses_system_identifier: Union[HousesSystemIdentifier, None] = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
201
- perspective_type: Union[PerspectiveType, None] = DEFAULT_PERSPECTIVE_TYPE,
202
- cache_expire_after_days: Union[int, None] = DEFAULT_GEONAMES_CACHE_EXPIRE_AFTER_DAYS,
203
- is_dst: Union[None, bool] = None,
204
- disable_chiron_and_lilith: bool = False
205
- ) -> None:
206
- logging.debug("Starting Kerykeion")
207
-
208
- # Deprecation warnings --->
209
- if disable_chiron is not None:
210
- warnings.warn(
211
- "The 'disable_chiron' argument is deprecated and will be removed in a future version. "
212
- "Please use 'disable_chiron' instead.",
213
- DeprecationWarning
214
- )
215
-
216
- if disable_chiron_and_lilith:
217
- raise ValueError("Cannot specify both 'disable_chiron' and 'disable_chiron_and_lilith'. Use 'disable_chiron_and_lilith' only.")
218
-
219
- self.disable_chiron_and_lilith = disable_chiron
220
- # <--- Deprecation warnings
221
-
222
- self.name = name
223
- self.year = year
224
- self.month = month
225
- self.day = day
226
- self.hour = hour
227
- self.minute = minute
228
- self.online = online
229
- self.json_dir = Path.home()
230
- self.disable_chiron = disable_chiron
231
- self.sidereal_mode = sidereal_mode
232
- self.cache_expire_after_days = cache_expire_after_days
233
- self.is_dst = is_dst
234
- self.disable_chiron_and_lilith = disable_chiron_and_lilith
235
-
236
- #---------------#
237
- # General setup #
238
- #---------------#
239
-
240
- # Geonames username
241
- if geonames_username is None and online and (not lat or not lng or not tz_str):
242
- logging.warning(GEONAMES_DEFAULT_USERNAME_WARNING)
243
- self.geonames_username = DEFAULT_GEONAMES_USERNAME
244
- else:
245
- self.geonames_username = geonames_username # type: ignore
246
-
247
- # City
248
- if not city:
249
- self.city = "London"
250
- logging.info("No city specified, using London as default")
251
- else:
252
- self.city = city
253
-
254
- # Nation
255
- if not nation:
256
- self.nation = "GB"
257
- logging.info("No nation specified, using GB as default")
258
- else:
259
- self.nation = nation
260
-
261
- # Latitude
262
- if not lat and not self.online:
263
- self.lat = 51.5074
264
- logging.info("No latitude specified, using London as default")
265
- else:
266
- self.lat = lat # type: ignore
267
-
268
- # Longitude
269
- if not lng and not self.online:
270
- self.lng = 0
271
- logging.info("No longitude specified, using London as default")
272
- else:
273
- self.lng = lng # type: ignore
274
-
275
- # Timezone
276
- if (not self.online) and (not tz_str):
277
- raise KerykeionException("You need to set the coordinates and timezone if you want to use the offline mode!")
278
- else:
279
- self.tz_str = tz_str # type: ignore
280
-
281
- # Zodiac type
282
- if not zodiac_type:
283
- self.zodiac_type = DEFAULT_ZODIAC_TYPE
284
- else:
285
- self.zodiac_type = zodiac_type
286
-
287
- # Perspective type
288
- if not perspective_type:
289
- self.perspective_type = DEFAULT_PERSPECTIVE_TYPE
290
- else:
291
- self.perspective_type = perspective_type
292
-
293
- # Houses system identifier
294
- if not houses_system_identifier:
295
- self.houses_system_identifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER
296
- else:
297
- self.houses_system_identifier = houses_system_identifier
298
-
299
- # Cache expire after days
300
- if not cache_expire_after_days:
301
- self.cache_expire_after_days = DEFAULT_GEONAMES_CACHE_EXPIRE_AFTER_DAYS
302
- else:
303
- self.cache_expire_after_days = cache_expire_after_days
304
-
305
- #-----------------------#
306
- # Swiss Ephemeris setup #
307
- #-----------------------#
308
-
309
- # We set the swisseph path to the current directory
310
- swe.set_ephe_path(str(Path(__file__).parent.absolute() / "sweph"))
311
-
312
- # Flags for the Swiss Ephemeris
313
- self._iflag = swe.FLG_SWIEPH + swe.FLG_SPEED
314
-
315
- # Chart Perspective check and setup --->
316
- if self.perspective_type not in get_args(PerspectiveType):
317
- raise KerykeionException(f"\n* ERROR: '{self.perspective_type}' is NOT a valid chart perspective! Available perspectives are: *" + "\n" + str(get_args(PerspectiveType)))
318
-
319
- if self.perspective_type == "True Geocentric":
320
- self._iflag += swe.FLG_TRUEPOS
321
- elif self.perspective_type == "Heliocentric":
322
- self._iflag += swe.FLG_HELCTR
323
- elif self.perspective_type == "Topocentric":
324
- self._iflag += swe.FLG_TOPOCTR
325
- # geopos_is_set, for topocentric
326
- if (self.online) and (not self.tz_str) and (not self.lat) and (not self.lng):
327
- self._fetch_and_set_tz_and_coordinates_from_geonames()
328
- swe.set_topo(self.lng, self.lat, 0)
329
- # <--- Chart Perspective check and setup
330
-
331
- # House System check and setup --->
332
- if self.houses_system_identifier not in get_args(HousesSystemIdentifier):
333
- raise KerykeionException(f"\n* ERROR: '{self.houses_system_identifier}' is NOT a valid house system! Available systems are: *" + "\n" + str(get_args(HousesSystemIdentifier)))
334
-
335
- self.houses_system_name = swe.house_name(self.houses_system_identifier.encode('ascii'))
336
- # <--- House System check and setup
337
-
338
- # Zodiac Type and Sidereal mode checks and setup --->
339
- if zodiac_type and not zodiac_type in get_args(ZodiacType):
340
- raise KerykeionException(f"\n* ERROR: '{zodiac_type}' is NOT a valid zodiac type! Available types are: *" + "\n" + str(get_args(ZodiacType)))
341
-
342
- if self.sidereal_mode and self.zodiac_type == "Tropic":
343
- raise KerykeionException("You can't set a sidereal mode with a Tropic zodiac type!")
344
-
345
- if self.zodiac_type == "Sidereal" and not self.sidereal_mode:
346
- self.sidereal_mode = DEFAULT_SIDEREAL_MODE
347
- logging.info("No sidereal mode set, using default FAGAN_BRADLEY")
348
-
349
- if self.zodiac_type == "Sidereal":
350
- # Check if the sidereal mode is valid
351
-
352
- if not self.sidereal_mode or not self.sidereal_mode in get_args(SiderealMode):
353
- raise KerykeionException(f"\n* ERROR: '{self.sidereal_mode}' is NOT a valid sidereal mode! Available modes are: *" + "\n" + str(get_args(SiderealMode)))
354
-
355
- self._iflag += swe.FLG_SIDEREAL
356
- mode = "SIDM_" + self.sidereal_mode
357
- swe.set_sid_mode(getattr(swe, mode))
358
- logging.debug(f"Using sidereal mode: {mode}")
359
- # <--- Zodiac Type and Sidereal mode checks and setup
360
-
361
- #------------------------#
362
- # Start the calculations #
363
- #------------------------#
364
-
365
- # UTC, julian day and local time setup --->
366
- if (self.online) and (not self.tz_str) and (not self.lat) and (not self.lng):
367
- self._fetch_and_set_tz_and_coordinates_from_geonames()
368
-
369
- self.lat = check_and_adjust_polar_latitude(self.lat)
370
-
371
- # Local time to UTC
372
- local_time = pytz.timezone(self.tz_str)
373
- naive_datetime = datetime(self.year, self.month, self.day, self.hour, self.minute, 0)
374
-
375
- try:
376
- local_datetime = local_time.localize(naive_datetime, is_dst=self.is_dst)
377
- except pytz.exceptions.AmbiguousTimeError:
378
- raise KerykeionException("Ambiguous time! Please specify if the time is in DST or not with the is_dst argument.")
379
-
380
- utc_object = local_datetime.astimezone(pytz.utc)
381
- self.iso_formatted_utc_datetime = utc_object.isoformat()
382
-
383
- # ISO formatted local datetime
384
- self.iso_formatted_local_datetime = local_datetime.isoformat()
385
-
386
- # Julian day calculation
387
- utc_float_hour_with_minutes = utc_object.hour + (utc_object.minute / 60)
388
- self.julian_day = float(swe.julday(utc_object.year, utc_object.month, utc_object.day, utc_float_hour_with_minutes))
389
- # <--- UTC, julian day and local time setup
390
-
391
- # Planets and Houses setup
392
- self._initialize_houses()
393
- self._initialize_planets()
394
-
395
- # Lunar Phase
396
- self.lunar_phase = calculate_moon_phase(
397
- self.moon.abs_pos,
398
- self.sun.abs_pos
399
- )
400
-
401
- # Deprecated properties
402
- self.utc_time
403
- self.local_time
404
-
405
- def __str__(self) -> str:
406
- return f"Astrological data for: {self.name}, {self.iso_formatted_utc_datetime} UTC\nBirth location: {self.city}, Lat {self.lat}, Lon {self.lng}"
407
-
408
- def __repr__(self) -> str:
409
- return f"Astrological data for: {self.name}, {self.iso_formatted_utc_datetime} UTC\nBirth location: {self.city}, Lat {self.lat}, Lon {self.lng}"
410
-
411
- def __getitem__(self, item):
412
- return getattr(self, item)
413
-
414
- def get(self, item, default=None):
415
- return getattr(self, item, default)
416
-
417
- def _fetch_and_set_tz_and_coordinates_from_geonames(self) -> None:
418
- """Gets the nearest time zone for the calculation"""
419
- logging.info("Fetching timezone/coordinates from geonames")
420
-
421
- geonames = FetchGeonames(
422
- self.city,
423
- self.nation,
424
- username=self.geonames_username,
425
- cache_expire_after_days=self.cache_expire_after_days
426
- )
427
- self.city_data: dict[str, str] = geonames.get_serialized_data()
428
-
429
- if (
430
- not "countryCode" in self.city_data
431
- or not "timezonestr" in self.city_data
432
- or not "lat" in self.city_data
433
- or not "lng" in self.city_data
434
- ):
435
- raise KerykeionException("No data found for this city, try again! Maybe check your connection?")
436
-
437
- self.nation = self.city_data["countryCode"]
438
- self.lng = float(self.city_data["lng"])
439
- self.lat = float(self.city_data["lat"])
440
- self.tz_str = self.city_data["timezonestr"]
441
-
442
- def _initialize_houses(self) -> None:
443
- """
444
- Calculate positions and store them in dictionaries
445
-
446
- https://www.astro.com/faq/fq_fh_owhouse_e.htm
447
- https://github.com/jwmatthys/pd-swisseph/blob/master/swehouse.c#L685
448
- hsys = letter code for house system;
449
- A equal
450
- E equal
451
- B Alcabitius
452
- C Campanus
453
- D equal (MC)
454
- F Carter "Poli-Equatorial"
455
- G 36 Gauquelin sectors
456
- H horizon / azimut
457
- I Sunshine solution Treindl
458
- i Sunshine solution Makransky
459
- K Koch
460
- L Pullen SD "sinusoidal delta", ex Neo-Porphyry
461
- M Morinus
462
- N equal/1=Aries
463
- O Porphyry
464
- P Placidus
465
- Q Pullen SR "sinusoidal ratio"
466
- R Regiomontanus
467
- S Sripati
468
- T Polich/Page ("topocentric")
469
- U Krusinski-Pisa-Goelzer
470
- V equal Vehlow
471
- W equal, whole sign
472
- X axial rotation system/ Meridian houses
473
- Y APC houses
474
- """
475
-
476
- _ascmc = (-1.0, -1.0)
477
-
478
- if self.zodiac_type == "Sidereal":
479
- cusps, ascmc = swe.houses_ex(
480
- tjdut=self.julian_day,
481
- lat=self.lat, lon=self.lng,
482
- hsys=str.encode(self.houses_system_identifier),
483
- flags=swe.FLG_SIDEREAL
484
- )
485
- self._houses_degree_ut = cusps
486
- _ascmc = ascmc
487
-
488
- elif self.zodiac_type == "Tropic":
489
- cusps, ascmc = swe.houses(
490
- tjdut=self.julian_day, lat=self.lat,
491
- lon=self.lng,
492
- hsys=str.encode(self.houses_system_identifier)
493
- )
494
- self._houses_degree_ut = cusps
495
- _ascmc = ascmc
496
-
497
- else:
498
- raise KerykeionException("Not a valid zodiac type: " + self.zodiac_type)
499
-
500
- point_type: PointType = "House"
501
-
502
- # stores the house in singular dictionaries.
503
- self.first_house = get_kerykeion_point_from_degree(self._houses_degree_ut[0], "First_House", point_type=point_type)
504
- self.second_house = get_kerykeion_point_from_degree(self._houses_degree_ut[1], "Second_House", point_type=point_type)
505
- self.third_house = get_kerykeion_point_from_degree(self._houses_degree_ut[2], "Third_House", point_type=point_type)
506
- self.fourth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[3], "Fourth_House", point_type=point_type)
507
- self.fifth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[4], "Fifth_House", point_type=point_type)
508
- self.sixth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[5], "Sixth_House", point_type=point_type)
509
- self.seventh_house = get_kerykeion_point_from_degree(self._houses_degree_ut[6], "Seventh_House", point_type=point_type)
510
- self.eighth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[7], "Eighth_House", point_type=point_type)
511
- self.ninth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[8], "Ninth_House", point_type=point_type)
512
- self.tenth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[9], "Tenth_House", point_type=point_type)
513
- self.eleventh_house = get_kerykeion_point_from_degree(self._houses_degree_ut[10], "Eleventh_House", point_type=point_type)
514
- self.twelfth_house = get_kerykeion_point_from_degree(self._houses_degree_ut[11], "Twelfth_House", point_type=point_type)
515
-
516
- self.houses_names_list = list(get_args(Houses))
517
-
518
- # Deprecated
519
- self._houses_list = [
520
- self.first_house,
521
- self.second_house,
522
- self.third_house,
523
- self.fourth_house,
524
- self.fifth_house,
525
- self.sixth_house,
526
- self.seventh_house,
527
- self.eighth_house,
528
- self.ninth_house,
529
- self.tenth_house,
530
- self.eleventh_house,
531
- self.twelfth_house,
532
- ]
533
-
534
- # AxialCusps
535
- point_type: PointType = "AxialCusps"
536
-
537
- # Calculate ascendant and medium coeli
538
- self.ascendant = get_kerykeion_point_from_degree(_ascmc[0], "Ascendant", point_type=point_type)
539
- self.medium_coeli = get_kerykeion_point_from_degree(_ascmc[1], "Medium_Coeli", point_type=point_type)
540
- # For descendant and imum coeli there exist no Swiss Ephemeris library calculation function,
541
- # but they are simply opposite the the ascendant and medium coeli
542
- dsc_deg = math.fmod(_ascmc[0] + 180, 360)
543
- ic_deg = math.fmod(_ascmc[1] + 180, 360)
544
- self.descendant = get_kerykeion_point_from_degree(dsc_deg, "Descendant", point_type=point_type)
545
- self.imum_coeli = get_kerykeion_point_from_degree(ic_deg, "Imum_Coeli", point_type=point_type)
546
-
547
- def _initialize_planets(self) -> None:
548
- """Defines body positon in signs and information and
549
- stores them in dictionaries"""
550
-
551
- point_type: PointType = "Planet"
552
-
553
- sun_deg = swe.calc_ut(self.julian_day, 0, self._iflag)[0][0]
554
- moon_deg = swe.calc_ut(self.julian_day, 1, self._iflag)[0][0]
555
- mercury_deg = swe.calc_ut(self.julian_day, 2, self._iflag)[0][0]
556
- venus_deg = swe.calc_ut(self.julian_day, 3, self._iflag)[0][0]
557
- mars_deg = swe.calc_ut(self.julian_day, 4, self._iflag)[0][0]
558
- jupiter_deg = swe.calc_ut(self.julian_day, 5, self._iflag)[0][0]
559
- saturn_deg = swe.calc_ut(self.julian_day, 6, self._iflag)[0][0]
560
- uranus_deg = swe.calc_ut(self.julian_day, 7, self._iflag)[0][0]
561
- neptune_deg = swe.calc_ut(self.julian_day, 8, self._iflag)[0][0]
562
- pluto_deg = swe.calc_ut(self.julian_day, 9, self._iflag)[0][0]
563
- mean_node_deg = swe.calc_ut(self.julian_day, 10, self._iflag)[0][0]
564
- true_node_deg = swe.calc_ut(self.julian_day, 11, self._iflag)[0][0]
565
- # For south nodes there exist no Swiss Ephemeris library calculation function,
566
- # but they are simply opposite the north node.
567
- mean_south_node_deg = math.fmod(mean_node_deg + 180, 360)
568
- true_south_node_deg = math.fmod(true_node_deg + 180, 360)
569
-
570
- # AC/DC axis and MC/IC axis were already calculated previously...
571
-
572
- self.sun = get_kerykeion_point_from_degree(sun_deg, "Sun", point_type=point_type)
573
- self.moon = get_kerykeion_point_from_degree(moon_deg, "Moon", point_type=point_type)
574
- self.mercury = get_kerykeion_point_from_degree(mercury_deg, "Mercury", point_type=point_type)
575
- self.venus = get_kerykeion_point_from_degree(venus_deg, "Venus", point_type=point_type)
576
- self.mars = get_kerykeion_point_from_degree(mars_deg, "Mars", point_type=point_type)
577
- self.jupiter = get_kerykeion_point_from_degree(jupiter_deg, "Jupiter", point_type=point_type)
578
- self.saturn = get_kerykeion_point_from_degree(saturn_deg, "Saturn", point_type=point_type)
579
- self.uranus = get_kerykeion_point_from_degree(uranus_deg, "Uranus", point_type=point_type)
580
- self.neptune = get_kerykeion_point_from_degree(neptune_deg, "Neptune", point_type=point_type)
581
- self.pluto = get_kerykeion_point_from_degree(pluto_deg, "Pluto", point_type=point_type)
582
- self.mean_node = get_kerykeion_point_from_degree(mean_node_deg, "Mean_Node", point_type=point_type)
583
- self.true_node = get_kerykeion_point_from_degree(true_node_deg, "True_Node", point_type=point_type)
584
- self.mean_south_node = get_kerykeion_point_from_degree(mean_south_node_deg, "Mean_South_Node", point_type=point_type)
585
- self.true_south_node = get_kerykeion_point_from_degree(true_south_node_deg, "True_South_Node", point_type=point_type)
586
-
587
- # Note that in whole-sign house systems ac/dc or mc/ic axes may not align with house cusps.
588
- # Therefore, for the axes we need to calculate house positions explicitly too.
589
- self.ascendant.house = get_planet_house(self.ascendant.abs_pos, self._houses_degree_ut)
590
- self.descendant.house = get_planet_house(self.descendant.abs_pos, self._houses_degree_ut)
591
- self.medium_coeli.house = get_planet_house(self.medium_coeli.abs_pos, self._houses_degree_ut)
592
- self.imum_coeli.house = get_planet_house(self.imum_coeli.abs_pos, self._houses_degree_ut)
593
-
594
- self.sun.house = get_planet_house(sun_deg, self._houses_degree_ut)
595
- self.moon.house = get_planet_house(moon_deg, self._houses_degree_ut)
596
- self.mercury.house = get_planet_house(mercury_deg, self._houses_degree_ut)
597
- self.venus.house = get_planet_house(venus_deg, self._houses_degree_ut)
598
- self.mars.house = get_planet_house(mars_deg, self._houses_degree_ut)
599
- self.jupiter.house = get_planet_house(jupiter_deg, self._houses_degree_ut)
600
- self.saturn.house = get_planet_house(saturn_deg, self._houses_degree_ut)
601
- self.uranus.house = get_planet_house(uranus_deg, self._houses_degree_ut)
602
- self.neptune.house = get_planet_house(neptune_deg, self._houses_degree_ut)
603
- self.pluto.house = get_planet_house(pluto_deg, self._houses_degree_ut)
604
- self.mean_node.house = get_planet_house(mean_node_deg, self._houses_degree_ut)
605
- self.true_node.house = get_planet_house(true_node_deg, self._houses_degree_ut)
606
- self.mean_south_node.house = get_planet_house(mean_south_node_deg, self._houses_degree_ut)
607
- self.true_south_node.house = get_planet_house(true_south_node_deg, self._houses_degree_ut)
608
-
609
-
610
- # Deprecated
611
- planets_list = [
612
- self.sun,
613
- self.moon,
614
- self.mercury,
615
- self.venus,
616
- self.mars,
617
- self.jupiter,
618
- self.saturn,
619
- self.uranus,
620
- self.neptune,
621
- self.pluto,
622
- self.mean_node,
623
- self.true_node,
624
- self.mean_south_node,
625
- self.true_south_node,
626
- ]
627
-
628
- if not self.disable_chiron_and_lilith:
629
- chiron_deg = swe.calc_ut(self.julian_day, 15, self._iflag)[0][0]
630
- mean_lilith_deg = swe.calc_ut(self.julian_day, 12, self._iflag)[0][0]
631
-
632
- self.chiron = get_kerykeion_point_from_degree(chiron_deg, "Chiron", point_type=point_type)
633
- self.mean_lilith = get_kerykeion_point_from_degree(mean_lilith_deg, "Mean_Lilith", point_type=point_type)
634
-
635
- self.chiron.house = get_planet_house(chiron_deg, self._houses_degree_ut)
636
- self.mean_lilith.house = get_planet_house(mean_lilith_deg, self._houses_degree_ut)
637
-
638
- # Deprecated
639
- planets_list.append(self.chiron)
640
- planets_list.append(self.mean_lilith)
641
-
642
- else:
643
- self.chiron = None
644
- self.mean_lilith = None
645
-
646
- # FIXME: Update after removing planets_list
647
- self.planets_names_list = [planet["name"] for planet in planets_list]
648
- self.axial_cusps_names_list = [
649
- axis["name"] for axis in [self.ascendant, self.descendant, self.medium_coeli, self.imum_coeli]
650
- ]
651
-
652
- # Check in retrograde or not:
653
- for planet in planets_list:
654
- planet_number = get_number_from_name(planet["name"])
655
-
656
- # Swiss ephemeris library does not offer calculation of direction of south nodes.
657
- # But south nodes have same direction as north nodes. We can use those to calculate direction.
658
- if planet_number == 1000: # Number of Mean South Node
659
- planet_number = 10 # Number of Mean North Node
660
- elif planet_number == 1100: # Number of True South Node
661
- planet_number = 11 # Number of True North Node
662
-
663
-
664
- if swe.calc_ut(self.julian_day, planet_number, self._iflag)[0][3] < 0:
665
- planet["retrograde"] = True
666
- else:
667
- planet["retrograde"] = False
668
-
669
- # AC/DC and MC/IC axes are never retrograde. For consistency, set them to be not retrograde.
670
- self.ascendant.retrograde = False
671
- self.descendant.retrograde = False
672
- self.medium_coeli.retrograde = False
673
- self.imum_coeli.retrograde = False
674
-
675
-
676
- def json(self, dump=False, destination_folder: Union[str, None] = None, indent: Union[int, None] = None) -> str:
677
- """
678
- Dumps the Kerykeion object to a json string foramt,
679
- if dump=True also dumps to file located in destination
680
- or the home folder.
681
- """
682
-
683
- KrData = AstrologicalSubjectModel(**self.__dict__)
684
- json_string = KrData.model_dump_json(exclude_none=True, indent=indent)
685
-
686
- if dump:
687
- if destination_folder:
688
- destination_path = Path(destination_folder)
689
- json_path = destination_path / f"{self.name}_kerykeion.json"
690
-
691
- else:
692
- json_path = self.json_dir / f"{self.name}_kerykeion.json"
693
-
694
- with open(json_path, "w", encoding="utf-8") as file:
695
- file.write(json_string)
696
- logging.info(f"JSON file dumped in {json_path}.")
697
-
698
- return json_string
699
-
700
- def model(self) -> AstrologicalSubjectModel:
701
- """
702
- Creates a Pydantic model of the Kerykeion object.
703
- """
704
-
705
- return AstrologicalSubjectModel(**self.__dict__)
706
-
707
- @cached_property
708
- def utc_time(self) -> float:
709
- """
710
- Deprecated property, use iso_formatted_utc_datetime instead, will be removed in the future.
711
- Returns the UTC time as a float.
712
- """
713
- dt = datetime.fromisoformat(self.iso_formatted_utc_datetime)
714
-
715
- # Extract the hours, minutes, and seconds
716
- hours = dt.hour
717
- minutes = dt.minute
718
- seconds = dt.second + dt.microsecond / 1_000_000
719
-
720
- # Convert time to float hours
721
- float_time = hours + minutes / 60 + seconds / 3600
722
-
723
- return float_time
724
-
725
- @cached_property
726
- def local_time(self) -> float:
727
- """
728
- Deprecated property, use iso_formatted_local_datetime instead, will be removed in the future.
729
- Returns the local time as a float.
730
- """
731
- dt = datetime.fromisoformat(self.iso_formatted_local_datetime)
732
-
733
- # Extract the hours, minutes, and seconds
734
- hours = dt.hour
735
- minutes = dt.minute
736
- seconds = dt.second + dt.microsecond / 1_000_000
737
-
738
- # Convert time to float hours
739
- float_time = hours + minutes / 60 + seconds / 3600
740
-
741
- return float_time
742
-
743
-
744
- @staticmethod
745
- def get_from_iso_utc_time(
746
- name: str,
747
- iso_utc_time: str,
748
- city: str = "Greenwich",
749
- nation: str = "GB",
750
- tz_str: str = "Etc/GMT",
751
- online: bool = False,
752
- lng: Union[int, float] = 0,
753
- lat: Union[int, float] = 51.5074,
754
- geonames_username: str = DEFAULT_GEONAMES_USERNAME,
755
- zodiac_type: ZodiacType = DEFAULT_ZODIAC_TYPE,
756
- disable_chiron_and_lilith: bool = False,
757
- sidereal_mode: Union[SiderealMode, None] = None,
758
- houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
759
- perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE
760
-
761
- ) -> "AstrologicalSubject":
762
- """
763
- Creates an AstrologicalSubject object from an iso formatted UTC time.
764
- This method is offline by default, set online=True to fetch the timezone and coordinates from geonames.
765
-
766
- Args:
767
- - name (str): The name of the subject.
768
- - iso_utc_time (str): The iso formatted UTC time.
769
- - city (str, optional): City or location of birth. Defaults to "Greenwich".
770
- - nation (str, optional): Nation of birth. Defaults to "GB".
771
- - tz_str (str, optional): Timezone of the birth location. Defaults to "Etc/GMT".
772
- - online (bool, optional): Sets if you want to use the online mode, which fetches the timezone and coordinates from geonames.
773
- If you already have the coordinates and timezone, set this to False. Defaults to False.
774
- - lng (Union[int, float], optional): Longitude of the birth location. Defaults to 0 (Greenwich, London).
775
- - lat (Union[int, float], optional): Latitude of the birth location. Defaults to 51.5074 (Greenwich, London).
776
- - geonames_username (str, optional): The username for the geonames API. Note: Change this to your own username to avoid rate limits!
777
- You can get one for free here: https://www.geonames.org/login
778
- - zodiac_type (ZodiacType, optional): The zodiac type to use. Defaults to "Tropic".
779
- - disable_chiron_and_lilith: boolean representing if Chiron and Lilith should be disabled. Default is False.
780
- Chiron calculation can create some issues with the Swiss Ephemeris when the date is too far in the past.
781
- - sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
782
- The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
783
- Defaults to None.
784
- Available modes are visible in the SiderealMode Literal.
785
- - houses_system_identifier (HousesSystemIdentifier, optional): The system to use for the calculation of the houses.
786
- Defaults to "P" (Placidus).
787
- Available systems are visible in the HousesSystemIdentifier Literal.
788
- - perspective_type (PerspectiveType, optional): The perspective to use for the calculation of the chart.
789
- Defaults to "Apparent Geocentric".
790
-
791
- Returns:
792
- - AstrologicalSubject: The AstrologicalSubject object.
793
- """
794
- dt = datetime.fromisoformat(iso_utc_time)
795
-
796
- if online == True:
797
- if geonames_username == DEFAULT_GEONAMES_USERNAME:
798
- logging.warning(GEONAMES_DEFAULT_USERNAME_WARNING)
799
-
800
- geonames = FetchGeonames(
801
- city,
802
- nation,
803
- username=geonames_username,
804
- )
805
-
806
- city_data: dict[str, str] = geonames.get_serialized_data()
807
- lng = float(city_data["lng"])
808
- lat = float(city_data["lat"])
809
-
810
- subject = AstrologicalSubject(
811
- name=name,
812
- year=dt.year,
813
- month=dt.month,
814
- day=dt.day,
815
- hour=dt.hour,
816
- minute=dt.minute,
817
- city=city,
818
- nation=city,
819
- lng=lng,
820
- lat=lat,
821
- tz_str=tz_str,
822
- online=False,
823
- geonames_username=geonames_username,
824
- zodiac_type=zodiac_type,
825
- sidereal_mode=sidereal_mode,
826
- houses_system_identifier=houses_system_identifier,
827
- perspective_type=perspective_type,
828
- disable_chiron_and_lilith=disable_chiron_and_lilith
829
- )
830
-
831
- return subject
832
-
833
- if __name__ == "__main__":
834
- import json
835
- from kerykeion.utilities import setup_logging
836
-
837
- setup_logging(level="debug")
838
-
839
- # With Chiron enabled
840
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", zodiac_type=None)
841
- print(json.loads(johnny.json(dump=True)))