kerykeion 4.18.5__py3-none-any.whl → 4.20.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.

@@ -54,8 +54,18 @@ class NatalAspects:
54
54
  for first in range(len(active_points_list)):
55
55
  # Generates the aspects list without repetitions
56
56
  for second in range(first + 1, len(active_points_list)):
57
+ # South Node and North Node are always in opposition
58
+ nodes_pairs = {
59
+ ("True_Node", "True_South_Node"),
60
+ ("Mean_Node", "Mean_South_Node"),
61
+ ("True_South_Node", "True_Node"),
62
+ ("Mean_South_Node", "Mean_Node"),
63
+ }
64
+ if (active_points_list[first]["name"], active_points_list[second]["name"]) in nodes_pairs:
65
+ continue
66
+
57
67
  aspect = get_aspect_from_two_points(
58
- self.aspects_settings, active_points_list[first]["abs_pos"],
68
+ self.aspects_settings, active_points_list[first]["abs_pos"],
59
69
  active_points_list[second]["abs_pos"]
60
70
  )
61
71
 
@@ -132,12 +142,12 @@ if __name__ == "__main__":
132
142
 
133
143
  johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US")
134
144
 
135
- # All aspects
145
+ # All aspects as a list of dictionaries
136
146
  aspects = NatalAspects(johnny)
137
- print(aspects.all_aspects)
147
+ print([a.model_dump() for a in aspects.all_aspects])
138
148
 
139
149
  print("\n")
140
150
 
141
- # Relevant aspects
151
+ # Relevant aspects as a list of dictionaries
142
152
  aspects = NatalAspects(johnny)
143
- print(aspects.relevant_aspects)
153
+ print([a.model_dump() for a in aspects.relevant_aspects])
@@ -101,13 +101,10 @@ if __name__ == "__main__":
101
101
 
102
102
  setup_logging(level="debug")
103
103
 
104
- john = AstrologicalSubject("John", 1940, 10, 9, 18, 30, "Liverpool")
105
- yoko = AstrologicalSubject("Yoko", 1933, 2, 18, 18, 30, "Tokyo", "JP")
104
+ john = AstrologicalSubject("John", 1940, 10, 9, 10, 30, "Liverpool", "GB")
105
+ yoko = AstrologicalSubject("Yoko", 1933, 2, 18, 10, 30, "Tokyo", "JP")
106
106
 
107
107
  synastry_aspects = SynastryAspects(john, yoko)
108
108
 
109
- # All aspects
110
- print(synastry_aspects.all_aspects)
111
-
112
- # Relevant aspects
113
- print(synastry_aspects.relevant_aspects)
109
+ # All aspects as a list of dictionaries
110
+ print([aspect.dict() for aspect in synastry_aspects.all_aspects])
@@ -25,8 +25,8 @@ from kerykeion.kr_types import (
25
25
  Houses
26
26
  )
27
27
  from kerykeion.utilities import (
28
- get_number_from_name,
29
- get_kerykeion_point_from_degree,
28
+ get_number_from_name,
29
+ get_kerykeion_point_from_degree,
30
30
  get_planet_house,
31
31
  get_moon_emoji_from_phase_int,
32
32
  get_moon_phase_name_from_phase_int,
@@ -79,7 +79,7 @@ class AstrologicalSubject:
79
79
  - online (bool, optional): Sets if you want to use the online mode, which fetches the timezone and coordinates from geonames.
80
80
  If you already have the coordinates and timezone, set this to False. Defaults to True.
81
81
  - disable_chiron: Deprecated, use disable_chiron_and_lilith instead.
82
- - sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
82
+ - sidereal_mode (SiderealMode, optional): Also known as Ayanamsa.
83
83
  The mode to use for the sidereal zodiac, according to the Swiss Ephemeris.
84
84
  Defaults to "FAGAN_BRADLEY".
85
85
  Available modes are visible in the SiderealMode Literal.
@@ -139,6 +139,8 @@ class AstrologicalSubject:
139
139
  mean_node: KerykeionPointModel
140
140
  chiron: Union[KerykeionPointModel, None]
141
141
  mean_lilith: Union[KerykeionPointModel, None]
142
+ true_south_node: KerykeionPointModel
143
+ mean_south_node: KerykeionPointModel
142
144
 
143
145
  # Houses
144
146
  first_house: KerykeionPointModel
@@ -197,10 +199,10 @@ class AstrologicalSubject:
197
199
  "Please use 'disable_chiron' instead.",
198
200
  DeprecationWarning
199
201
  )
200
-
202
+
201
203
  if disable_chiron_and_lilith:
202
204
  raise ValueError("Cannot specify both 'disable_chiron' and 'disable_chiron_and_lilith'. Use 'disable_chiron_and_lilith' only.")
203
-
205
+
204
206
  self.disable_chiron_and_lilith = disable_chiron
205
207
  # <--- Deprecation warnings
206
208
 
@@ -225,7 +227,7 @@ class AstrologicalSubject:
225
227
  #---------------#
226
228
 
227
229
  # Geonames username
228
- if geonames_username is None and online:
230
+ if geonames_username is None and online and (not lat or not lng or not tz_str):
229
231
  logging.warning(GEONAMES_DEFAULT_USERNAME_WARNING)
230
232
  self.geonames_username = DEFAULT_GEONAMES_USERNAME
231
233
  else:
@@ -251,7 +253,7 @@ class AstrologicalSubject:
251
253
  logging.info("No latitude specified, using London as default")
252
254
  else:
253
255
  self.lat = lat # type: ignore
254
-
256
+
255
257
  # Longitude
256
258
  if not lng and not self.online:
257
259
  self.lng = 0
@@ -278,7 +280,7 @@ class AstrologicalSubject:
278
280
  # Chart Perspective check and setup --->
279
281
  if self.perspective_type not in get_args(PerspectiveType):
280
282
  raise KerykeionException(f"\n* ERROR: '{self.perspective_type}' is NOT a valid chart perspective! Available perspectives are: *" + "\n" + str(get_args(PerspectiveType)))
281
-
283
+
282
284
  if self.perspective_type == "True Geocentric":
283
285
  self._iflag += swe.FLG_TRUEPOS
284
286
  elif self.perspective_type == "Heliocentric":
@@ -304,14 +306,14 @@ class AstrologicalSubject:
304
306
 
305
307
  if self.sidereal_mode and self.zodiac_type == "Tropic":
306
308
  raise KerykeionException("You can't set a sidereal mode with a Tropic zodiac type!")
307
-
309
+
308
310
  if self.zodiac_type == "Sidereal" and not self.sidereal_mode:
309
311
  self.sidereal_mode = DEFAULT_SIDEREAL_MODE
310
312
  logging.info("No sidereal mode set, using default FAGAN_BRADLEY")
311
313
 
312
314
  if self.zodiac_type == "Sidereal":
313
315
  # Check if the sidereal mode is valid
314
-
316
+
315
317
  if not self.sidereal_mode or not self.sidereal_mode in get_args(SiderealMode):
316
318
  raise KerykeionException(f"\n* ERROR: '{self.sidereal_mode}' is NOT a valid sidereal mode! Available modes are: *" + "\n" + str(get_args(SiderealMode)))
317
319
 
@@ -328,7 +330,7 @@ class AstrologicalSubject:
328
330
  # UTC, julian day and local time setup --->
329
331
  if (self.online) and (not self.tz_str) and (not self.lat) and (not self.lng):
330
332
  self._fetch_and_set_tz_and_coordinates_from_geonames()
331
-
333
+
332
334
  self.lat = check_and_adjust_polar_latitude(self.lat)
333
335
 
334
336
  # Local time to UTC
@@ -496,6 +498,10 @@ class AstrologicalSubject:
496
498
  pluto_deg = swe.calc(self.julian_day, 9, self._iflag)[0][0]
497
499
  mean_node_deg = swe.calc(self.julian_day, 10, self._iflag)[0][0]
498
500
  true_node_deg = swe.calc(self.julian_day, 11, self._iflag)[0][0]
501
+ # For south nodes there exist no Swiss Ephemeris library calculation function,
502
+ # but they are simply opposite the north node.
503
+ mean_south_node_deg = (mean_node_deg + 180) % 360
504
+ true_south_node_deg = (true_node_deg + 180) % 360
499
505
 
500
506
  self.sun = get_kerykeion_point_from_degree(sun_deg, "Sun", point_type=point_type)
501
507
  self.moon = get_kerykeion_point_from_degree(moon_deg, "Moon", point_type=point_type)
@@ -509,6 +515,8 @@ class AstrologicalSubject:
509
515
  self.pluto = get_kerykeion_point_from_degree(pluto_deg, "Pluto", point_type=point_type)
510
516
  self.mean_node = get_kerykeion_point_from_degree(mean_node_deg, "Mean_Node", point_type=point_type)
511
517
  self.true_node = get_kerykeion_point_from_degree(true_node_deg, "True_Node", point_type=point_type)
518
+ self.mean_south_node = get_kerykeion_point_from_degree(mean_south_node_deg, "Mean_South_Node", point_type=point_type)
519
+ self.true_south_node = get_kerykeion_point_from_degree(true_south_node_deg, "True_South_Node", point_type=point_type)
512
520
 
513
521
  self.sun.house = get_planet_house(sun_deg, self._houses_degree_ut)
514
522
  self.moon.house = get_planet_house(moon_deg, self._houses_degree_ut)
@@ -522,6 +530,9 @@ class AstrologicalSubject:
522
530
  self.pluto.house = get_planet_house(pluto_deg, self._houses_degree_ut)
523
531
  self.mean_node.house = get_planet_house(mean_node_deg, self._houses_degree_ut)
524
532
  self.true_node.house = get_planet_house(true_node_deg, self._houses_degree_ut)
533
+ self.mean_south_node.house = get_planet_house(mean_south_node_deg, self._houses_degree_ut)
534
+ self.true_south_node.house = get_planet_house(true_south_node_deg, self._houses_degree_ut)
535
+
525
536
 
526
537
  # Deprecated
527
538
  planets_list = [
@@ -537,6 +548,8 @@ class AstrologicalSubject:
537
548
  self.pluto,
538
549
  self.mean_node,
539
550
  self.true_node,
551
+ self.mean_south_node,
552
+ self.true_south_node,
540
553
  ]
541
554
 
542
555
  if not self.disable_chiron_and_lilith:
@@ -563,6 +576,14 @@ class AstrologicalSubject:
563
576
  # Check in retrograde or not:
564
577
  for planet in planets_list:
565
578
  planet_number = get_number_from_name(planet["name"])
579
+
580
+ # Swiss ephemeris library does not offer calculation of direction of south nodes.
581
+ # But south nodes have same direction as north nodes. We can use those to calculate direction.
582
+ if planet_number == 1000: # Number of Mean South Node
583
+ planet_number = 10 # Number of Mean North Node
584
+ elif planet_number == 1100: # Number of True South Node
585
+ planet_number = 11 # Number of True North Node
586
+
566
587
  if swe.calc(self.julian_day, planet_number, self._iflag)[0][3] < 0:
567
588
  planet["retrograde"] = True
568
589
  else:
@@ -651,7 +672,7 @@ class AstrologicalSubject:
651
672
  Returns the UTC time as a float.
652
673
  """
653
674
  dt = datetime.fromisoformat(self.iso_formatted_utc_datetime)
654
-
675
+
655
676
  # Extract the hours, minutes, and seconds
656
677
  hours = dt.hour
657
678
  minutes = dt.minute
@@ -669,7 +690,7 @@ class AstrologicalSubject:
669
690
  Returns the local time as a float.
670
691
  """
671
692
  dt = datetime.fromisoformat(self.iso_formatted_local_datetime)
672
-
693
+
673
694
  # Extract the hours, minutes, and seconds
674
695
  hours = dt.hour
675
696
  minutes = dt.minute
@@ -684,8 +705,8 @@ class AstrologicalSubject:
684
705
  @staticmethod
685
706
  def get_from_iso_utc_time(
686
707
  name: str,
687
- iso_utc_time: str,
688
- city: str = "Greenwich",
708
+ iso_utc_time: str,
709
+ city: str = "Greenwich",
689
710
  nation: str = "GB",
690
711
  tz_str: str = "Etc/GMT",
691
712
  online: bool = False,
@@ -697,7 +718,7 @@ class AstrologicalSubject:
697
718
  sidereal_mode: Union[SiderealMode, None] = None,
698
719
  houses_system_identifier: HousesSystemIdentifier = DEFAULT_HOUSES_SYSTEM_IDENTIFIER,
699
720
  perspective_type: PerspectiveType = DEFAULT_PERSPECTIVE_TYPE
700
-
721
+
701
722
  ) -> "AstrologicalSubject":
702
723
  """
703
724
  Creates an AstrologicalSubject object from an iso formatted UTC time.
@@ -775,7 +796,7 @@ if __name__ == "__main__":
775
796
  from kerykeion.utilities import setup_logging
776
797
 
777
798
  setup_logging(level="debug")
778
-
799
+
779
800
  # With Chiron enabled
780
801
  johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US")
781
802
  print(json.loads(johnny.json(dump=True)))
@@ -814,5 +835,5 @@ if __name__ == "__main__":
814
835
  print(johnny.mean_lilith)
815
836
 
816
837
  # Offline mode
817
- johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", online=False, tz_str="America/New_York", lng=-87.1111, lat=37.7711)
838
+ johnny = AstrologicalSubject("Johnny Depp", 1963, 6, 9, 0, 0, "Owensboro", "US", online=False, tz_str="America/New_York", lng=-87.1111, lat=37.7711, sidereal_mode="FAGAN_BRADLEY", zodiac_type="Sidereal")
818
839
  print(johnny.json(dump=True, indent=2))
@@ -34,7 +34,8 @@ def get_decoded_kerykeion_celestial_point_name(input_planet_name: str, celestial
34
34
  return celestial_point_language[input_planet_name]
35
35
 
36
36
  # Return the special house name if it exists, otherwise return an empty string
37
- return special_house_names.get(input_planet_name, "")
37
+ decoded_special_name = special_house_names.get(input_planet_name, "")
38
+ return celestial_point_language[decoded_special_name]
38
39
 
39
40
 
40
41
  def decHourJoin(inH: int, inM: int, inS: int) -> float:
@@ -515,8 +516,8 @@ def draw_third_circle(
515
516
 
516
517
 
517
518
  def draw_aspect_grid(
518
- stroke_color: str,
519
- available_planets: list,
519
+ stroke_color: str,
520
+ available_planets: list,
520
521
  aspects: list,
521
522
  x_start: int = 380,
522
523
  y_start: int = 468,
@@ -705,7 +706,7 @@ def draw_transit_aspect_list(
705
706
  Returns:
706
707
  - A string containing the SVG path data for the aspect transit grid.
707
708
  """
708
-
709
+
709
710
  if isinstance(celestial_point_language, dict):
710
711
  celestial_point_language = KerykeionLanguageCelestialPointModel(**celestial_point_language)
711
712
 
@@ -719,26 +720,29 @@ def draw_transit_aspect_list(
719
720
  line = 0
720
721
  nl = 0
721
722
  inner_path = ""
722
- scale = 1
723
723
  for i, aspect in enumerate(aspects_list):
724
724
  # Adjust the vertical position for every 12 aspects
725
- if i == 12:
725
+ if i == 14:
726
726
  nl = 100
727
727
  line = 0
728
728
 
729
- elif i == 24:
729
+ elif i == 28:
730
730
  nl = 200
731
731
  line = 0
732
732
 
733
- elif i == 36:
733
+ elif i == 42:
734
734
  nl = 300
735
735
  line = 0
736
736
 
737
- elif i == 48:
737
+ elif i == 56:
738
738
  nl = 400
739
+ line = 0
740
+
741
+ elif i == 70:
742
+ nl = 500
739
743
  # When there are more than 60 aspects, the text is moved up
740
- if len(aspects_list) > 60:
741
- line = -1 * (len(aspects_list) - 60) * 14
744
+ if len(aspects_list) > 84:
745
+ line = -1 * (len(aspects_list) - 84) * 14
742
746
  else:
743
747
  line = 0
744
748
 
@@ -761,7 +765,7 @@ def draw_transit_aspect_list(
761
765
  inner_path += f"</g>"
762
766
  line = line + 14
763
767
 
764
- out = f'<g style="transform: translate(47%, 59%) scale({scale})">'
768
+ out = f'<g style="transform: translate(43%, 50%)">'
765
769
  out += f'<text y="-15" x="0" style="fill: var(--kerykeion-chart-color-paper-0); font-size: 14px;">{grid_title}:</text>'
766
770
  out += inner_path
767
771
  out += "</g>"
@@ -863,11 +867,11 @@ def draw_house_grid(
863
867
  Returns:
864
868
  - str: The SVG code for the grid of houses.
865
869
  """
866
-
870
+
867
871
  if chart_type in ["Synastry", "Transit"] and secondary_subject_houses_list is None:
868
872
  raise KerykeionException("secondary_houses is None")
869
873
 
870
- svg_output = '<g transform="translate(610,-20)">'
874
+ svg_output = '<g transform="translate(650,-20)">'
871
875
 
872
876
  line_increment = 10
873
877
  for i, house in enumerate(main_subject_houses_list):
@@ -885,7 +889,7 @@ def draw_house_grid(
885
889
 
886
890
  if chart_type == "Synastry":
887
891
  svg_output += '<!-- Synastry Houses -->'
888
- svg_output += '<g transform="translate(850, -20)">'
892
+ svg_output += '<g transform="translate(910, -20)">'
889
893
  line_increment = 10
890
894
 
891
895
  for i, house in enumerate(secondary_subject_houses_list): # type: ignore
@@ -935,8 +939,7 @@ def draw_planet_grid(
935
939
  offset_between_lines = 14
936
940
 
937
941
  svg_output = (
938
- f'<g transform="translate(510,-20)">'
939
- f'<g transform="translate(140, -15)">'
942
+ f'<g transform="translate(175, -15)">'
940
943
  f'<text text-anchor="end" style="fill:{text_color}; font-size: 14px;">{planets_and_houses_grid_title} {subject_name}:</text>'
941
944
  f'</g>'
942
945
  )
@@ -1003,13 +1006,12 @@ def draw_planet_grid(
1003
1006
  svg_output += end_of_line
1004
1007
  second_line_height += offset_between_lines
1005
1008
 
1006
- svg_output += end_of_line
1007
1009
  return svg_output
1008
1010
 
1009
1011
 
1010
1012
  def draw_transit_aspect_grid(
1011
- stroke_color: str,
1012
- available_planets: list,
1013
+ stroke_color: str,
1014
+ available_planets: list,
1013
1015
  aspects: list,
1014
1016
  x_indent: int = 50,
1015
1017
  y_indent: int = 250,
@@ -1080,4 +1082,4 @@ def draw_transit_aspect_grid(
1080
1082
  if (aspect["p1"] == planet_a["id"] and aspect["p2"] == planet_b["id"]):
1081
1083
  svg_output += f'<use x="{x_aspect - box_size + 1}" y="{y_aspect + 1}" xlink:href="#orb{aspect["aspect_degrees"]}" />'
1082
1084
 
1083
- return svg_output
1085
+ return svg_output
@@ -5,6 +5,7 @@
5
5
 
6
6
 
7
7
  import logging
8
+ import swisseph as swe
8
9
  from typing import get_args
9
10
 
10
11
  from kerykeion.settings.kerykeion_settings import get_settings
@@ -17,8 +18,8 @@ from kerykeion.kr_types.kr_models import AstrologicalSubjectModel
17
18
  from kerykeion.kr_types.settings_models import KerykeionSettingsCelestialPointModel, KerykeionSettingsModel
18
19
  from kerykeion.kr_types.kr_literals import KerykeionChartTheme, KerykeionChartLanguage
19
20
  from kerykeion.charts.charts_utils import (
20
- draw_zodiac_slice,
21
- convert_latitude_coordinate_to_string,
21
+ draw_zodiac_slice,
22
+ convert_latitude_coordinate_to_string,
22
23
  convert_longitude_coordinate_to_string,
23
24
  draw_aspect_line,
24
25
  draw_elements_percentages,
@@ -62,13 +63,16 @@ class KerykeionChartSVG:
62
63
  - double_chart_aspect_grid_type: Set the type of the aspect grid for the double chart (transit or synastry). (Default: list.)
63
64
  - chart_language: Set the language for the chart (default: EN).
64
65
  """
65
-
66
+
66
67
  # Constants
67
- _BASIC_CHART_VIEWBOX = "0 0 772.2 546.0"
68
- _WIDE_CHART_VIEWBOX = "0 0 1060 546.0"
69
- _DEFAULT_HEIGHT = 546.0
68
+ _BASIC_CHART_VIEWBOX = "0 0 820 550.0"
69
+ _WIDE_CHART_VIEWBOX = "0 0 1200 546.0"
70
+ _TRANSIT_CHART_WITH_TABLE_VIWBOX = "0 0 960 546.0"
71
+
72
+ _DEFAULT_HEIGHT = 550
70
73
  _DEFAULT_FULL_WIDTH = 1200
71
- _DEFAULT_NATAL_WIDTH = 772.2
74
+ _DEFAULT_NATAL_WIDTH = 820
75
+ _DEFAULT_FULL_WIDTH_WITH_TABLE = 960
72
76
  _PLANET_IN_ZODIAC_EXTRA_POINTS = 10
73
77
 
74
78
  # Set at init
@@ -170,6 +174,8 @@ class KerykeionChartSVG:
170
174
  self.height = self._DEFAULT_HEIGHT
171
175
  if self.chart_type == "Synastry" or self.chart_type == "Transit":
172
176
  self.width = self._DEFAULT_FULL_WIDTH
177
+ elif self.double_chart_aspect_grid_type == "table" and self.chart_type == "Transit":
178
+ self.width = self._DEFAULT_FULL_WIDTH_WITH_TABLE
173
179
  else:
174
180
  self.width = self._DEFAULT_NATAL_WIDTH
175
181
 
@@ -177,7 +183,7 @@ class KerykeionChartSVG:
177
183
  self.location = self.user.city
178
184
  self.geolat = self.user.lat
179
185
  self.geolon = self.user.lng
180
-
186
+
181
187
  if self.chart_type == "Transit":
182
188
  self.t_name = self.language_settings["transit_name"]
183
189
 
@@ -284,7 +290,7 @@ class KerykeionChartSVG:
284
290
  {"name": "Aqu", "element": "air"},
285
291
  {"name": "Pis", "element": "water"},
286
292
  )
287
-
293
+
288
294
  # Available bodies
289
295
  available_celestial_points_names = []
290
296
  for body in self.available_planets_setting:
@@ -364,7 +370,12 @@ class KerykeionChartSVG:
364
370
  template_dict["stringName"] = f"{self.user.name}:" if self.chart_type in ["Synastry", "Transit"] else f'{self.language_settings["info"]}:'
365
371
 
366
372
  # Set viewbox based on chart type
367
- template_dict['viewbox'] = self._BASIC_CHART_VIEWBOX if self.chart_type in ["Natal", "ExternalNatal"] else self._WIDE_CHART_VIEWBOX
373
+ if self.chart_type in ["Natal", "ExternalNatal"]:
374
+ template_dict['viewbox'] = self._BASIC_CHART_VIEWBOX
375
+ elif self.double_chart_aspect_grid_type == "table" and self.chart_type == "Transit":
376
+ template_dict['viewbox'] = self._TRANSIT_CHART_WITH_TABLE_VIWBOX
377
+ else:
378
+ template_dict['viewbox'] = self._WIDE_CHART_VIEWBOX
368
379
 
369
380
  # Generate rings and circles based on chart type
370
381
  if self.chart_type in ["Transit", "Synastry"]:
@@ -398,11 +409,19 @@ class KerykeionChartSVG:
398
409
  else:
399
410
  template_dict["stringTitle"] = self.user.name
400
411
 
401
- # Set bottom left corner information
402
- template_dict["bottomLeft0"] = f"{self.user.zodiac_type if self.user.zodiac_type == 'Tropic' else str(self.user.zodiac_type) + ' ' + str(self.user.sidereal_mode)}"
403
- template_dict["bottomLeft1"] = f"{self.user.houses_system_name}"
412
+ if self.user.zodiac_type == 'Tropic':
413
+ zodiac_info = "Tropical Zodiac"
414
+
415
+ else:
416
+ mode_const = "SIDM_" + self.user.sidereal_mode # type: ignore
417
+ mode_name = swe.get_ayanamsa_name(getattr(swe, mode_const))
418
+ zodiac_info = f"Ayanamsa: {mode_name}"
419
+
420
+ template_dict["bottomLeft0"] = f"{self.user.houses_system_name} Houses"
421
+ template_dict["bottomLeft1"] = zodiac_info
422
+
404
423
  if self.chart_type in ["Natal", "ExternalNatal", "Synastry"]:
405
- template_dict["bottomLeft2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get("day", "Day")} {self.user.lunar_phase.get("moon_phase", "")}'
424
+ template_dict["bottomLeft2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")} {self.language_settings.get("day", "Day").lower()}: {self.user.lunar_phase.get("moon_phase", "")}'
406
425
  template_dict["bottomLeft3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.user.lunar_phase.moon_phase_name}'
407
426
  template_dict["bottomLeft4"] = f'{self.user.perspective_type}'
408
427
  else:
@@ -412,7 +431,7 @@ class KerykeionChartSVG:
412
431
 
413
432
  # Draw moon phase
414
433
  template_dict['moon_phase'] = draw_moon_phase(
415
- self.user.lunar_phase["degrees_between_s_m"],
434
+ self.user.lunar_phase["degrees_between_s_m"],
416
435
  self.geolat
417
436
  )
418
437
 
@@ -513,20 +532,20 @@ class KerykeionChartSVG:
513
532
  # Draw planets
514
533
  if self.chart_type in ["Transit", "Synastry"]:
515
534
  template_dict["makePlanets"] = draw_planets(
516
- available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
535
+ available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
517
536
  available_planets_setting=self.available_planets_setting,
518
537
  second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
519
- radius=self.main_radius,
538
+ radius=self.main_radius,
520
539
  main_subject_first_house_degree_ut=self.user.first_house.abs_pos,
521
540
  main_subject_seventh_house_degree_ut=self.user.seventh_house.abs_pos,
522
- chart_type=self.chart_type,
541
+ chart_type=self.chart_type,
523
542
  third_circle_radius=self.third_circle_radius,
524
543
  )
525
544
  else:
526
545
  template_dict["makePlanets"] = draw_planets(
527
546
  available_planets_setting=self.available_planets_setting,
528
547
  chart_type=self.chart_type,
529
- radius=self.main_radius,
548
+ radius=self.main_radius,
530
549
  available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
531
550
  third_circle_radius=self.third_circle_radius,
532
551
  main_subject_first_house_degree_ut=self.user.first_house.abs_pos,
@@ -585,7 +604,7 @@ class KerykeionChartSVG:
585
604
 
586
605
  DATA_DIR = Path(__file__).parent
587
606
  xml_svg = DATA_DIR / "templates" / "chart.xml"
588
-
607
+
589
608
  # read template
590
609
  with open(xml_svg, "r", encoding="utf-8", errors="ignore") as f:
591
610
  template = Template(f.read()).substitute(td)
@@ -619,16 +638,16 @@ class KerykeionChartSVG:
619
638
 
620
639
  def makeWheelOnlyTemplate(self, minify: bool = False):
621
640
  """Creates the template for the SVG file with only the wheel"""
622
-
641
+
623
642
  with open(Path(__file__).parent / "templates" / "wheel_only.xml", "r", encoding="utf-8", errors="ignore") as f:
624
643
  template = f.read()
625
644
 
626
645
  template_dict = self._create_template_dictionary()
627
646
  template = Template(template).substitute(template_dict)
628
-
647
+
629
648
  if minify:
630
649
  template = scourString(template).replace('"', "'").replace("\n", "").replace("\t","").replace(" ", "").replace(" ", "")
631
-
650
+
632
651
  else:
633
652
  template = template.replace('"', "'")
634
653
 
@@ -647,7 +666,7 @@ class KerykeionChartSVG:
647
666
 
648
667
  def makeAspectGridOnlyTemplate(self, minify: bool = False):
649
668
  """Creates the template for the SVG file with only the aspect grid"""
650
-
669
+
651
670
  with open(Path(__file__).parent / "templates" / "aspect_grid_only.xml", "r", encoding="utf-8", errors="ignore") as f:
652
671
  template = f.read()
653
672
 
@@ -659,10 +678,10 @@ class KerykeionChartSVG:
659
678
  aspects_grid = draw_aspect_grid(self.chart_colors_settings['paper_0'], self.available_planets_setting, self.aspects_list, x_start=50, y_start=250)
660
679
 
661
680
  template = Template(template).substitute({**template_dict, "makeAspectGrid": aspects_grid})
662
-
681
+
663
682
  if minify:
664
683
  template = scourString(template).replace('"', "'").replace("\n", "").replace("\t","").replace(" ", "").replace(" ", "")
665
-
684
+
666
685
  else:
667
686
  template = template.replace('"', "'")
668
687
 
@@ -702,7 +721,7 @@ if __name__ == "__main__":
702
721
  # Transits Chart
703
722
  transits_chart = KerykeionChartSVG(first, "Transit", second)
704
723
  transits_chart.makeSVG()
705
-
724
+
706
725
  # Sidereal Birth Chart (Lahiri)
707
726
  sidereal_subject = AstrologicalSubject("John Lennon Lahiri", 1940, 10, 9, 18, 30, "Liverpool", "GB", zodiac_type="Sidereal", sidereal_mode="LAHIRI")
708
727
  sidereal_chart = KerykeionChartSVG(sidereal_subject)
@@ -841,6 +860,11 @@ if __name__ == "__main__":
841
860
  aspect_grid_dark_synastry_chart = KerykeionChartSVG(aspect_grid_dark_synastry_subject, "Synastry", second, theme="dark")
842
861
  aspect_grid_dark_synastry_chart.makeAspectGridOnlySVG()
843
862
 
863
+ # Synastry Chart With draw_transit_aspect_list table
864
+ synastry_chart_with_table_list_subject = AstrologicalSubject("John Lennon - SCTWL", 1940, 10, 9, 18, 30, "Liverpool", "GB")
865
+ synastry_chart_with_table_list = KerykeionChartSVG(synastry_chart_with_table_list_subject, "Synastry", second, double_chart_aspect_grid_type="list", theme="dark")
866
+ synastry_chart_with_table_list.makeSVG()
867
+
844
868
  # Transit Chart With draw_transit_aspect_grid table
845
869
  transit_chart_with_table_grid_subject = AstrologicalSubject("John Lennon - TCWTG", 1940, 10, 9, 18, 30, "Liverpool", "GB")
846
870
  transit_chart_with_table_grid = KerykeionChartSVG(transit_chart_with_table_grid_subject, "Transit", second, double_chart_aspect_grid_type="table", theme="dark")
@@ -889,4 +913,9 @@ if __name__ == "__main__":
889
913
  # Hindi Language Chart
890
914
  hindi_subject = AstrologicalSubject("Amitabh Bachchan", 1942, 10, 11, 4, 0, "Allahabad", "IN")
891
915
  hindi_chart = KerykeionChartSVG(hindi_subject, chart_language="HI")
892
- hindi_chart.makeSVG()
916
+ hindi_chart.makeSVG()
917
+
918
+ # Kanye West Natal Chart
919
+ kanye_west_subject = AstrologicalSubject("Kanye", 1977, 6, 8, 8, 45, "Atlanta", "US")
920
+ kanye_west_chart = KerykeionChartSVG(kanye_west_subject)
921
+ kanye_west_chart.makeSVG()
@@ -7,7 +7,7 @@ OpenAstro.org -->
7
7
  xmlns:kr="https://www.kerykeion.net/"
8
8
  width="100%"
9
9
  height="100%"
10
- viewBox="40 38 230 230"
10
+ viewBox="28 20 255 250"
11
11
  preserveAspectRatio="xMidYMid"
12
12
  style="background-color: $paper_color_1"
13
13
  >
@@ -146,6 +146,24 @@ OpenAstro.org -->
146
146
  />
147
147
  </g>
148
148
  </symbol>
149
+ <symbol id="Mean_South_Node">
150
+ <g transform="translate(2,5)">
151
+ <g transform="scale(.75)">
152
+ <path
153
+ d="M 13.891439,23.899148 C 16.831061,23.16167 19.42871,20.957929 20.393204,18.051853 C 21.020409,16.655337 20.990829,15.041667 20.533362,13.600675 C 20.114218,11.835116 19.107819,10.284901 18.004613,8.8731359 C 17.010334,7.1652064 16.695528,4.9968194 17.378257,3.1191925 C 17.758616,2.134839 18.612459,1.1290194 19.747609,1.122205 C 20.856714,1.1522691 21.68807,2.1940507 21.762984,3.2502572 C 21.912267,4.7681974 20.969993,6.4261707 19.471746,6.8713331 C 19.067889,7.1616711 18.25164,6.6059847 18.085179,6.9788263 C 19.290894,8.1433226 21.294758,8.0903554 22.639135,7.1933294 C 23.815746,6.4142182 24.244753,4.8354574 23.866042,3.513777 C 23.579596,1.951978 22.299181,0.68814445 20.784404,0.28003765 C 19.338156,-0.21325355 17.636132,-0.074257846 16.389677,0.85208495 C 14.815514,1.7718962 14.147431,3.6918765 14.163087,5.4368764 C 14.158082,6.4844752 14.521263,7.4854719 14.829092,8.4735115 C 15.207397,9.5849835 15.743054,10.635344 16.373553,11.623268 C 17.391254,13.444769 18.097991,15.578055 17.73759,17.680935 C 17.264619,20.303885 14.771995,22.367369 12.114619,22.391402 C 10.221592,22.570108 8.4623977,21.408784 7.3640695,19.946934 C 5.9800493,18.024601 5.7632677,15.423089 6.5407261,13.21741 C 7.0447364,11.683217 7.9039267,10.303136 8.7551602,8.9442496 C 9.3673378,7.7170077 9.5453858,6.3050768 9.4520188,4.9498232 C 9.3273658,3.5827422 9.055649,2.055641 7.9264908,1.1408811 C 5.9170924,-0.45587205 2.5879904,-0.21634735 1.0121097,1.878869 C -0.10699561,3.3638861 -0.077071356,5.7321616 1.3439679,7.0142862 C 2.2628957,7.7723325 3.5523542,8.1172364 4.7082249,7.7533106 C 5.2902186,7.5823499 5.8305214,7.2547717 6.2290478,6.7948231 C 5.2075081,6.7604711 3.9811354,6.8872988 3.2192579,6.0602046 C 2.4586494,4.9643087 2.1899922,3.4530691 2.7277339,2.20348 C 3.1104121,1.3003657 4.160788,0.64048665 5.1397626,0.98349485 C 6.2468062,1.3370711 6.9552792,2.4455554 7.0537996,3.5696899 C 7.3808409,5.492555 7.1315,7.5939422 5.9619608,9.2041629 C 5.2025142,10.29701 4.2871193,11.315357 3.8562083,12.598809 C 2.7918212,15.240251 3.4299247,18.367596 5.1702811,20.571791 C 6.8286269,22.84876 9.6781638,24.154892 12.482992,23.997045 C 12.953784,23.989488 13.423977,23.955037 13.891439,23.899148 z "
154
+ style="fill: $planets_color_10" />
155
+ </g>
156
+ </g>
157
+ </symbol>
158
+ <symbol id="True_South_Node">
159
+ <g transform="translate(2,5)">
160
+ <g transform="scale(.75)">
161
+ <path
162
+ d="M 13.891439,23.899148 C 16.831061,23.16167 19.42871,20.957929 20.393204,18.051853 C 21.020409,16.655337 20.990829,15.041667 20.533362,13.600675 C 20.114218,11.835116 19.107819,10.284901 18.004613,8.8731359 C 17.010334,7.1652064 16.695528,4.9968194 17.378257,3.1191925 C 17.758616,2.134839 18.612459,1.1290194 19.747609,1.122205 C 20.856714,1.1522691 21.68807,2.1940507 21.762984,3.2502572 C 21.912267,4.7681974 20.969993,6.4261707 19.471746,6.8713331 C 19.067889,7.1616711 18.25164,6.6059847 18.085179,6.9788263 C 19.290894,8.1433226 21.294758,8.0903554 22.639135,7.1933294 C 23.815746,6.4142182 24.244753,4.8354574 23.866042,3.513777 C 23.579596,1.951978 22.299181,0.68814445 20.784404,0.28003765 C 19.338156,-0.21325355 17.636132,-0.074257846 16.389677,0.85208495 C 14.815514,1.7718962 14.147431,3.6918765 14.163087,5.4368764 C 14.158082,6.4844752 14.521263,7.4854719 14.829092,8.4735115 C 15.207397,9.5849835 15.743054,10.635344 16.373553,11.623268 C 17.391254,13.444769 18.097991,15.578055 17.73759,17.680935 C 17.264619,20.303885 14.771995,22.367369 12.114619,22.391402 C 10.221592,22.570108 8.4623977,21.408784 7.3640695,19.946934 C 5.9800493,18.024601 5.7632677,15.423089 6.5407261,13.21741 C 7.0447364,11.683217 7.9039267,10.303136 8.7551602,8.9442496 C 9.3673378,7.7170077 9.5453858,6.3050768 9.4520188,4.9498232 C 9.3273658,3.5827422 9.055649,2.055641 7.9264908,1.1408811 C 5.9170924,-0.45587205 2.5879904,-0.21634735 1.0121097,1.878869 C -0.10699561,3.3638861 -0.077071356,5.7321616 1.3439679,7.0142862 C 2.2628957,7.7723325 3.5523542,8.1172364 4.7082249,7.7533106 C 5.2902186,7.5823499 5.8305214,7.2547717 6.2290478,6.7948231 C 5.2075081,6.7604711 3.9811354,6.8872988 3.2192579,6.0602046 C 2.4586494,4.9643087 2.1899922,3.4530691 2.7277339,2.20348 C 3.1104121,1.3003657 4.160788,0.64048665 5.1397626,0.98349485 C 6.2468062,1.3370711 6.9552792,2.4455554 7.0537996,3.5696899 C 7.3808409,5.492555 7.1315,7.5939422 5.9619608,9.2041629 C 5.2025142,10.29701 4.2871193,11.315357 3.8562083,12.598809 C 2.7918212,15.240251 3.4299247,18.367596 5.1702811,20.571791 C 6.8286269,22.84876 9.6781638,24.154892 12.482992,23.997045 C 12.953784,23.989488 13.423977,23.955037 13.891439,23.899148 z "
163
+ style="fill: $planets_color_11" />
164
+ </g>
165
+ </g>
166
+ </symbol>
149
167
  <symbol id="Chiron">
150
168
  <path
151
169
  d="M 10.019873,13.068981 L 10.185542,0.80951431 M 10.195591,6.7442901 L 15.496983,2.2712408 M 10.214831,6.6321726 L 15.516217,11.105216 M 17.192934,17.998774 C 17.192934,20.8646 14.867052,23.190487 12.001226,23.190487 C 9.1353988,23.190487 6.8095128,20.8646 6.8095128,17.998774 C 6.8095128,15.132953 9.1353988,12.807066 12.001226,12.807066 C 14.867052,12.807066 17.192934,15.132953 17.192934,17.998774 z "
@@ -449,4 +467,4 @@ OpenAstro.org -->
449
467
  />
450
468
  </symbol>
451
469
  </defs>
452
- </svg>
470
+ </svg>