kerykeion 4.14.4__py3-none-any.whl → 4.14.6__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/charts/charts_utils.py +201 -4
- kerykeion/charts/draw_planets.py +406 -0
- kerykeion/charts/kerykeion_chart_svg.py +238 -744
- kerykeion/charts/templates/chart.xml +13 -20
- kerykeion/kr_types/chart_types.py +1 -7
- kerykeion/kr_types/kr_literals.py +1 -1
- kerykeion/kr_types/settings_models.py +6 -2
- {kerykeion-4.14.4.dist-info → kerykeion-4.14.6.dist-info}/METADATA +1 -1
- {kerykeion-4.14.4.dist-info → kerykeion-4.14.6.dist-info}/RECORD +12 -11
- {kerykeion-4.14.4.dist-info → kerykeion-4.14.6.dist-info}/LICENSE +0 -0
- {kerykeion-4.14.4.dist-info → kerykeion-4.14.6.dist-info}/WHEEL +0 -0
- {kerykeion-4.14.4.dist-info → kerykeion-4.14.6.dist-info}/entry_points.txt +0 -0
kerykeion/charts/charts_utils.py
CHANGED
|
@@ -2,10 +2,41 @@ import math
|
|
|
2
2
|
import datetime
|
|
3
3
|
from kerykeion.kr_types import KerykeionException, ChartType
|
|
4
4
|
from typing import Union
|
|
5
|
-
from kerykeion.kr_types.kr_models import AspectModel
|
|
5
|
+
from kerykeion.kr_types.kr_models import AspectModel, KerykeionPointModel
|
|
6
6
|
from kerykeion.kr_types.settings_models import KerykeionLanguageCelestialPointModel, KerykeionSettingsAspectModel
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
def get_decoded_kerykeion_celestial_point_name(input_planet_name: str, celestial_point_language: KerykeionLanguageCelestialPointModel) -> str:
|
|
10
|
+
"""
|
|
11
|
+
Decode the given celestial point name based on the provided language model.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
input_planet_name (str): The name of the celestial point to decode.
|
|
15
|
+
celestial_point_language (KerykeionLanguageCelestialPointModel): The language model containing celestial point names.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: The decoded celestial point name.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Dictionary for special house names
|
|
22
|
+
special_house_names = {
|
|
23
|
+
"First_House": "Asc",
|
|
24
|
+
"Seventh_House": "Dsc",
|
|
25
|
+
"Tenth_House": "Mc",
|
|
26
|
+
"Fourth_House": "Ic"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Get the language model keys
|
|
30
|
+
language_keys = celestial_point_language.model_dump().keys()
|
|
31
|
+
|
|
32
|
+
# Check if the input planet name exists in the language model
|
|
33
|
+
if input_planet_name in language_keys:
|
|
34
|
+
return celestial_point_language[input_planet_name]
|
|
35
|
+
|
|
36
|
+
# Return the special house name if it exists, otherwise return an empty string
|
|
37
|
+
return special_house_names.get(input_planet_name, "")
|
|
38
|
+
|
|
39
|
+
|
|
9
40
|
def decHourJoin(inH: int, inM: int, inS: int) -> float:
|
|
10
41
|
"""Join hour, minutes, seconds, timezone integer to hour float.
|
|
11
42
|
|
|
@@ -132,10 +163,10 @@ def draw_zodiac_slice(
|
|
|
132
163
|
- seventh_house_degree_ut (Union[int, float]): The degree of the seventh house.
|
|
133
164
|
- num (int): The number of the sign. Note: In OpenAstro it did refer to self.zodiac,
|
|
134
165
|
which is a list of the signs in order, starting with Aries. Eg:
|
|
135
|
-
{"name": "
|
|
166
|
+
{"name": "Ari", "element": "fire"}
|
|
136
167
|
- r (Union[int, float]): The value of r.
|
|
137
168
|
- style (str): The CSS inline style.
|
|
138
|
-
- type (str): The type ?. In OpenAstro, it was the symbol of the sign. Eg: "
|
|
169
|
+
- type (str): The type ?. In OpenAstro, it was the symbol of the sign. Eg: "Ari".
|
|
139
170
|
self.zodiac[i]["name"]
|
|
140
171
|
|
|
141
172
|
Returns:
|
|
@@ -802,8 +833,174 @@ def draw_moon_phase(
|
|
|
802
833
|
# Return the SVG element as a string
|
|
803
834
|
return (
|
|
804
835
|
f'<g transform="rotate({lunar_phase_rotate} 20 10)">'
|
|
836
|
+
f' <defs>'
|
|
837
|
+
f' <clipPath id="moonPhaseCutOffCircle">'
|
|
838
|
+
f' <circle cx="20" cy="10" r="10" />'
|
|
839
|
+
f' </clipPath>'
|
|
840
|
+
f' </defs>'
|
|
805
841
|
f' <circle cx="20" cy="10" r="10" style="fill: {fill_color_background}" />'
|
|
806
842
|
f' <circle cx="{circle_center_x}" cy="10" r="{circle_radius}" style="fill: {fill_color_foreground}" clip-path="url(#moonPhaseCutOffCircle)" />'
|
|
807
843
|
f' <circle cx="20" cy="10" r="10" style="fill: none; stroke: {lunar_phase_outline_color}; stroke-width: 0.5px; stroke-opacity: 0.5" />'
|
|
808
|
-
f
|
|
844
|
+
f'</g>'
|
|
809
845
|
)
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def draw_house_grid(
|
|
849
|
+
main_subject_houses_list: list[KerykeionPointModel],
|
|
850
|
+
chart_type: ChartType,
|
|
851
|
+
secondary_subject_houses_list: Union[list[KerykeionPointModel], None] = None,
|
|
852
|
+
text_color: str = "#000000",
|
|
853
|
+
house_cusp_generale_name_label: str = "Cusp",
|
|
854
|
+
) -> str:
|
|
855
|
+
"""
|
|
856
|
+
Generate SVG code for a grid of astrological houses.
|
|
857
|
+
|
|
858
|
+
Parameters:
|
|
859
|
+
- main_houses (list[KerykeionPointModel]): List of houses for the main subject.
|
|
860
|
+
- chart_type (ChartType): Type of the chart (e.g., Synastry, Transit).
|
|
861
|
+
- secondary_houses (list[KerykeionPointModel], optional): List of houses for the secondary subject.
|
|
862
|
+
- text_color (str): Color of the text.
|
|
863
|
+
- cusp_label (str): Label for the house cusp.
|
|
864
|
+
|
|
865
|
+
Returns:
|
|
866
|
+
- str: The SVG code for the grid of houses.
|
|
867
|
+
"""
|
|
868
|
+
|
|
869
|
+
if chart_type in ["Synastry", "Transit"] and secondary_subject_houses_list is None:
|
|
870
|
+
raise KerykeionException("secondary_houses is None")
|
|
871
|
+
|
|
872
|
+
svg_output = '<g transform="translate(610,-20)">'
|
|
873
|
+
|
|
874
|
+
line_increment = 10
|
|
875
|
+
for i, house in enumerate(main_subject_houses_list):
|
|
876
|
+
cusp_number = f"  {i + 1}" if i < 9 else str(i + 1)
|
|
877
|
+
svg_output += (
|
|
878
|
+
f'<g transform="translate(0,{line_increment})">'
|
|
879
|
+
f'<text text-anchor="end" x="40" style="fill:{text_color}; font-size: 10px;">{house_cusp_generale_name_label} {cusp_number}:</text>'
|
|
880
|
+
f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{house["sign"]}" /></g>'
|
|
881
|
+
f'<text x="53" style="fill:{text_color}; font-size: 10px;"> {convert_decimal_to_degree_string(house["position"])}</text>'
|
|
882
|
+
f'</g>'
|
|
883
|
+
)
|
|
884
|
+
line_increment += 14
|
|
885
|
+
|
|
886
|
+
svg_output += "</g>"
|
|
887
|
+
|
|
888
|
+
if chart_type == "Synastry":
|
|
889
|
+
svg_output += '<!-- Synastry Houses -->'
|
|
890
|
+
svg_output += '<g transform="translate(850, -20)">'
|
|
891
|
+
line_increment = 10
|
|
892
|
+
|
|
893
|
+
for i, house in enumerate(secondary_subject_houses_list):
|
|
894
|
+
cusp_number = f"  {i + 1}" if i < 9 else str(i + 1)
|
|
895
|
+
svg_output += (
|
|
896
|
+
f'<g transform="translate(0,{line_increment})">'
|
|
897
|
+
f'<text text-anchor="end" x="40" style="fill:{text_color}; font-size: 10px;">{house_cusp_generale_name_label} {cusp_number}:</text>'
|
|
898
|
+
f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{house["sign"]}" /></g>'
|
|
899
|
+
f'<text x="53" style="fill:{text_color}; font-size: 10px;"> {convert_decimal_to_degree_string(house["position"])}</text>'
|
|
900
|
+
f'</g>'
|
|
901
|
+
)
|
|
902
|
+
line_increment += 14
|
|
903
|
+
|
|
904
|
+
svg_output += "</g>"
|
|
905
|
+
|
|
906
|
+
return svg_output
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
def draw_planet_grid(
|
|
910
|
+
planets_and_houses_grid_title: str,
|
|
911
|
+
subject_name: str,
|
|
912
|
+
available_kerykeion_celestial_points: list[KerykeionPointModel],
|
|
913
|
+
chart_type: ChartType,
|
|
914
|
+
celestial_point_language: KerykeionLanguageCelestialPointModel,
|
|
915
|
+
second_subject_name: str = None,
|
|
916
|
+
second_subject_available_kerykeion_celestial_points: list[KerykeionPointModel] = None,
|
|
917
|
+
text_color: str = "#000000",
|
|
918
|
+
) -> str:
|
|
919
|
+
"""
|
|
920
|
+
Draws the planet grid for the given celestial points and chart type.
|
|
921
|
+
|
|
922
|
+
Args:
|
|
923
|
+
planets_and_houses_grid_title (str): Title of the grid.
|
|
924
|
+
subject_name (str): Name of the subject.
|
|
925
|
+
available_kerykeion_celestial_points (list[KerykeionPointModel]): List of celestial points for the subject.
|
|
926
|
+
chart_type (ChartType): Type of the chart.
|
|
927
|
+
celestial_point_language (KerykeionLanguageCelestialPointModel): Language model for celestial points.
|
|
928
|
+
second_subject_name (str, optional): Name of the second subject. Defaults to None.
|
|
929
|
+
second_subject_available_kerykeion_celestial_points (list[KerykeionPointModel], optional): List of celestial points for the second subject. Defaults to None.
|
|
930
|
+
text_color (str, optional): Color of the text. Defaults to "#000000".
|
|
931
|
+
|
|
932
|
+
Returns:
|
|
933
|
+
str: The SVG output for the planet grid.
|
|
934
|
+
"""
|
|
935
|
+
line_height = 10
|
|
936
|
+
offset = 0
|
|
937
|
+
offset_between_lines = 14
|
|
938
|
+
|
|
939
|
+
svg_output = (
|
|
940
|
+
f'<g transform="translate(510,-20)">'
|
|
941
|
+
f'<g transform="translate(140, -15)">'
|
|
942
|
+
f'<text text-anchor="end" style="fill:{text_color}; font-size: 14px;">{planets_and_houses_grid_title} {subject_name}:</text>'
|
|
943
|
+
f'</g>'
|
|
944
|
+
)
|
|
945
|
+
|
|
946
|
+
end_of_line = "</g>"
|
|
947
|
+
|
|
948
|
+
for i, planet in enumerate(available_kerykeion_celestial_points):
|
|
949
|
+
if i == 27:
|
|
950
|
+
line_height = 10
|
|
951
|
+
offset = -120
|
|
952
|
+
|
|
953
|
+
decoded_name = get_decoded_kerykeion_celestial_point_name(planet["name"], celestial_point_language)
|
|
954
|
+
svg_output += (
|
|
955
|
+
f'<g transform="translate({offset},{line_height})">'
|
|
956
|
+
f'<text text-anchor="end" style="fill:{text_color}; font-size: 10px;">{decoded_name}</text>'
|
|
957
|
+
f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{planet["name"]}" /></g>'
|
|
958
|
+
f'<text text-anchor="start" x="19" style="fill:{text_color}; font-size: 10px;">{convert_decimal_to_degree_string(planet["position"])}</text>'
|
|
959
|
+
f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{planet["sign"]}" /></g>'
|
|
960
|
+
)
|
|
961
|
+
|
|
962
|
+
if planet["retrograde"]:
|
|
963
|
+
svg_output += '<g transform="translate(74,-6)"><use transform="scale(.5)" xlink:href="#retrograde" /></g>'
|
|
964
|
+
|
|
965
|
+
svg_output += end_of_line
|
|
966
|
+
line_height += offset_between_lines
|
|
967
|
+
|
|
968
|
+
if chart_type in ["Transit", "Synastry"]:
|
|
969
|
+
if chart_type == "Transit":
|
|
970
|
+
svg_output += (
|
|
971
|
+
f'<g transform="translate(320, -15)">'
|
|
972
|
+
f'<text text-anchor="end" style="fill:{text_color}; font-size: 14px;">{second_subject_name}:</text>'
|
|
973
|
+
)
|
|
974
|
+
else:
|
|
975
|
+
svg_output += (
|
|
976
|
+
f'<g transform="translate(380, -15)">'
|
|
977
|
+
f'<text text-anchor="end" style="fill:{text_color}; font-size: 14px;">{planets_and_houses_grid_title} {second_subject_name}:</text>'
|
|
978
|
+
)
|
|
979
|
+
|
|
980
|
+
svg_output += end_of_line
|
|
981
|
+
|
|
982
|
+
second_line_height = 10
|
|
983
|
+
second_offset = 250
|
|
984
|
+
|
|
985
|
+
for i, t_planet in enumerate(second_subject_available_kerykeion_celestial_points):
|
|
986
|
+
if i == 27:
|
|
987
|
+
second_line_height = 10
|
|
988
|
+
second_offset = -120
|
|
989
|
+
|
|
990
|
+
second_decoded_name = get_decoded_kerykeion_celestial_point_name(t_planet["name"], celestial_point_language)
|
|
991
|
+
svg_output += (
|
|
992
|
+
f'<g transform="translate({second_offset},{second_line_height})">'
|
|
993
|
+
f'<text text-anchor="end" style="fill:{text_color}; font-size: 10px;">{second_decoded_name}</text>'
|
|
994
|
+
f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{t_planet["name"]}" /></g>'
|
|
995
|
+
f'<text text-anchor="start" x="19" style="fill:{text_color}; font-size: 10px;">{convert_decimal_to_degree_string(t_planet["position"])}</text>'
|
|
996
|
+
f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{t_planet["sign"]}" /></g>'
|
|
997
|
+
)
|
|
998
|
+
|
|
999
|
+
if t_planet["retrograde"]:
|
|
1000
|
+
svg_output += '<g transform="translate(74,-6)"><use transform="scale(.5)" xlink:href="#retrograde" /></g>'
|
|
1001
|
+
|
|
1002
|
+
svg_output += end_of_line
|
|
1003
|
+
second_line_height += offset_between_lines
|
|
1004
|
+
|
|
1005
|
+
svg_output += end_of_line
|
|
1006
|
+
return svg_output
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
from kerykeion.charts.charts_utils import degreeDiff, sliceToX, sliceToY, convert_decimal_to_degree_string
|
|
2
|
+
from kerykeion.kr_types import KerykeionException, ChartType, KerykeionPointModel
|
|
3
|
+
from kerykeion.kr_types.settings_models import KerykeionSettingsCelestialPointModel
|
|
4
|
+
from kerykeion.kr_types.kr_literals import Houses
|
|
5
|
+
import logging
|
|
6
|
+
from typing import Union, get_args
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def draw_planets(
|
|
12
|
+
radius: Union[int, float],
|
|
13
|
+
available_kerykeion_celestial_points: list[KerykeionPointModel],
|
|
14
|
+
available_planets_setting: list[KerykeionSettingsCelestialPointModel],
|
|
15
|
+
third_circle_radius: Union[int, float],
|
|
16
|
+
main_subject_first_house_degree_ut: Union[int, float],
|
|
17
|
+
main_subject_seventh_house_degree_ut: Union[int, float],
|
|
18
|
+
chart_type: ChartType,
|
|
19
|
+
second_subject_available_kerykeion_celestial_points: Union[list[KerykeionPointModel], None] = None,
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Draws the planets on a chart based on the provided parameters.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
radius (int): The radius of the chart.
|
|
26
|
+
available_kerykeion_celestial_points (list[KerykeionPointModel]): List of celestial points for the main subject.
|
|
27
|
+
available_planets_setting (list[KerykeionSettingsCelestialPointModel]): Settings for the celestial points.
|
|
28
|
+
third_circle_radius (Union[int, float]): Radius of the third circle.
|
|
29
|
+
main_subject_first_house_degree_ut (Union[int, float]): Degree of the first house for the main subject.
|
|
30
|
+
main_subject_seventh_house_degree_ut (Union[int, float]): Degree of the seventh house for the main subject.
|
|
31
|
+
chart_type (ChartType): Type of the chart (e.g., "Transit", "Synastry").
|
|
32
|
+
second_subject_available_kerykeion_celestial_points (Union[list[KerykeionPointModel], None], optional):
|
|
33
|
+
List of celestial points for the second subject, required for "Transit" or "Synastry" charts. Defaults to None.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
KerykeionException: If the second subject is required but not provided.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
str: SVG output for the chart with the planets drawn.
|
|
40
|
+
"""
|
|
41
|
+
TRANSIT_RING_EXCLUDE_POINTS_NAMES = get_args(Houses)
|
|
42
|
+
|
|
43
|
+
if chart_type == "Transit" or chart_type == "Synastry":
|
|
44
|
+
if second_subject_available_kerykeion_celestial_points is None:
|
|
45
|
+
raise KerykeionException("Second subject is required for Transit or Synastry charts")
|
|
46
|
+
|
|
47
|
+
# Make a list for the absolute degrees of the points of the graphic.
|
|
48
|
+
points_deg_ut = []
|
|
49
|
+
for planet in available_kerykeion_celestial_points:
|
|
50
|
+
points_deg_ut.append(planet.abs_pos)
|
|
51
|
+
|
|
52
|
+
# Make a list of the relative degrees of the points in the graphic.
|
|
53
|
+
points_deg = []
|
|
54
|
+
for planet in available_kerykeion_celestial_points:
|
|
55
|
+
points_deg.append(planet.position)
|
|
56
|
+
|
|
57
|
+
if chart_type == "Transit" or chart_type == "Synastry":
|
|
58
|
+
# Make a list for the absolute degrees of the points of the graphic.
|
|
59
|
+
t_points_deg_ut = []
|
|
60
|
+
for planet in second_subject_available_kerykeion_celestial_points:
|
|
61
|
+
t_points_deg_ut.append(planet.abs_pos)
|
|
62
|
+
|
|
63
|
+
# Make a list of the relative degrees of the points in the graphic.
|
|
64
|
+
t_points_deg = []
|
|
65
|
+
for planet in second_subject_available_kerykeion_celestial_points:
|
|
66
|
+
t_points_deg.append(planet.position)
|
|
67
|
+
|
|
68
|
+
planets_degut = {}
|
|
69
|
+
diff = range(len(available_planets_setting))
|
|
70
|
+
|
|
71
|
+
for i in range(len(available_planets_setting)):
|
|
72
|
+
# list of planets sorted by degree
|
|
73
|
+
logging.debug(f"planet: {i}, degree: {points_deg_ut[i]}")
|
|
74
|
+
planets_degut[points_deg_ut[i]] = i
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
FIXME: The planets_degut is a dictionary like:
|
|
78
|
+
{planet_degree: planet_index}
|
|
79
|
+
It should be replaced bu points_deg_ut
|
|
80
|
+
print(points_deg_ut)
|
|
81
|
+
print(planets_degut)
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
output = ""
|
|
85
|
+
keys = list(planets_degut.keys())
|
|
86
|
+
keys.sort()
|
|
87
|
+
switch = 0
|
|
88
|
+
|
|
89
|
+
planets_degrouped = {}
|
|
90
|
+
groups = []
|
|
91
|
+
planets_by_pos = list(range(len(planets_degut)))
|
|
92
|
+
planet_drange = 3.4
|
|
93
|
+
# get groups closely together
|
|
94
|
+
group_open = False
|
|
95
|
+
for e in range(len(keys)):
|
|
96
|
+
i = planets_degut[keys[e]]
|
|
97
|
+
# get distances between planets
|
|
98
|
+
if e == 0:
|
|
99
|
+
prev = points_deg_ut[planets_degut[keys[-1]]]
|
|
100
|
+
next = points_deg_ut[planets_degut[keys[1]]]
|
|
101
|
+
elif e == (len(keys) - 1):
|
|
102
|
+
prev = points_deg_ut[planets_degut[keys[e - 1]]]
|
|
103
|
+
next = points_deg_ut[planets_degut[keys[0]]]
|
|
104
|
+
else:
|
|
105
|
+
prev = points_deg_ut[planets_degut[keys[e - 1]]]
|
|
106
|
+
next = points_deg_ut[planets_degut[keys[e + 1]]]
|
|
107
|
+
diffa = degreeDiff(prev, points_deg_ut[i])
|
|
108
|
+
diffb = degreeDiff(next, points_deg_ut[i])
|
|
109
|
+
planets_by_pos[e] = [i, diffa, diffb]
|
|
110
|
+
|
|
111
|
+
logging.debug(f'{available_planets_setting[i]["label"]}, {diffa}, {diffb}')
|
|
112
|
+
|
|
113
|
+
if diffb < planet_drange:
|
|
114
|
+
if group_open:
|
|
115
|
+
groups[-1].append([e, diffa, diffb, available_planets_setting[planets_degut[keys[e]]]["label"]])
|
|
116
|
+
else:
|
|
117
|
+
group_open = True
|
|
118
|
+
groups.append([])
|
|
119
|
+
groups[-1].append([e, diffa, diffb, available_planets_setting[planets_degut[keys[e]]]["label"]])
|
|
120
|
+
else:
|
|
121
|
+
if group_open:
|
|
122
|
+
groups[-1].append([e, diffa, diffb, available_planets_setting[planets_degut[keys[e]]]["label"]])
|
|
123
|
+
group_open = False
|
|
124
|
+
|
|
125
|
+
def zero(x):
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
planets_delta = list(map(zero, range(len(available_planets_setting))))
|
|
129
|
+
|
|
130
|
+
# print groups
|
|
131
|
+
# print planets_by_pos
|
|
132
|
+
for a in range(len(groups)):
|
|
133
|
+
# Two grouped planets
|
|
134
|
+
if len(groups[a]) == 2:
|
|
135
|
+
next_to_a = groups[a][0][0] - 1
|
|
136
|
+
if groups[a][1][0] == (len(planets_by_pos) - 1):
|
|
137
|
+
next_to_b = 0
|
|
138
|
+
else:
|
|
139
|
+
next_to_b = groups[a][1][0] + 1
|
|
140
|
+
# if both planets have room
|
|
141
|
+
if (groups[a][0][1] > (2 * planet_drange)) & (groups[a][1][2] > (2 * planet_drange)):
|
|
142
|
+
planets_delta[groups[a][0][0]] = -(planet_drange - groups[a][0][2]) / 2
|
|
143
|
+
planets_delta[groups[a][1][0]] = +(planet_drange - groups[a][0][2]) / 2
|
|
144
|
+
# if planet a has room
|
|
145
|
+
elif groups[a][0][1] > (2 * planet_drange):
|
|
146
|
+
planets_delta[groups[a][0][0]] = -planet_drange
|
|
147
|
+
# if planet b has room
|
|
148
|
+
elif groups[a][1][2] > (2 * planet_drange):
|
|
149
|
+
planets_delta[groups[a][1][0]] = +planet_drange
|
|
150
|
+
|
|
151
|
+
# if planets next to a and b have room move them
|
|
152
|
+
elif (planets_by_pos[next_to_a][1] > (2.4 * planet_drange)) & (
|
|
153
|
+
planets_by_pos[next_to_b][2] > (2.4 * planet_drange)
|
|
154
|
+
):
|
|
155
|
+
planets_delta[(next_to_a)] = groups[a][0][1] - planet_drange * 2
|
|
156
|
+
planets_delta[groups[a][0][0]] = -planet_drange * 0.5
|
|
157
|
+
planets_delta[next_to_b] = -(groups[a][1][2] - planet_drange * 2)
|
|
158
|
+
planets_delta[groups[a][1][0]] = +planet_drange * 0.5
|
|
159
|
+
|
|
160
|
+
# if planet next to a has room move them
|
|
161
|
+
elif planets_by_pos[next_to_a][1] > (2 * planet_drange):
|
|
162
|
+
planets_delta[(next_to_a)] = groups[a][0][1] - planet_drange * 2.5
|
|
163
|
+
planets_delta[groups[a][0][0]] = -planet_drange * 1.2
|
|
164
|
+
|
|
165
|
+
# if planet next to b has room move them
|
|
166
|
+
elif planets_by_pos[next_to_b][2] > (2 * planet_drange):
|
|
167
|
+
planets_delta[next_to_b] = -(groups[a][1][2] - planet_drange * 2.5)
|
|
168
|
+
planets_delta[groups[a][1][0]] = +planet_drange * 1.2
|
|
169
|
+
|
|
170
|
+
# Three grouped planets or more
|
|
171
|
+
xl = len(groups[a])
|
|
172
|
+
if xl >= 3:
|
|
173
|
+
available = groups[a][0][1]
|
|
174
|
+
for f in range(xl):
|
|
175
|
+
available += groups[a][f][2]
|
|
176
|
+
need = (3 * planet_drange) + (1.2 * (xl - 1) * planet_drange)
|
|
177
|
+
leftover = available - need
|
|
178
|
+
xa = groups[a][0][1]
|
|
179
|
+
xb = groups[a][(xl - 1)][2]
|
|
180
|
+
|
|
181
|
+
# center
|
|
182
|
+
if (xa > (need * 0.5)) & (xb > (need * 0.5)):
|
|
183
|
+
startA = xa - (need * 0.5)
|
|
184
|
+
# position relative to next planets
|
|
185
|
+
else:
|
|
186
|
+
startA = (leftover / (xa + xb)) * xa
|
|
187
|
+
startB = (leftover / (xa + xb)) * xb
|
|
188
|
+
|
|
189
|
+
if available > need:
|
|
190
|
+
planets_delta[groups[a][0][0]] = startA - groups[a][0][1] + (1.5 * planet_drange)
|
|
191
|
+
for f in range(xl - 1):
|
|
192
|
+
planets_delta[groups[a][(f + 1)][0]] = (
|
|
193
|
+
1.2 * planet_drange + planets_delta[groups[a][f][0]] - groups[a][f][2]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
for e in range(len(keys)):
|
|
197
|
+
i = planets_degut[keys[e]]
|
|
198
|
+
|
|
199
|
+
# coordinates
|
|
200
|
+
if chart_type == "Transit" or chart_type == "Synastry":
|
|
201
|
+
if 22 < i < 27:
|
|
202
|
+
rplanet = 76
|
|
203
|
+
elif switch == 1:
|
|
204
|
+
rplanet = 110
|
|
205
|
+
switch = 0
|
|
206
|
+
else:
|
|
207
|
+
rplanet = 130
|
|
208
|
+
switch = 1
|
|
209
|
+
else:
|
|
210
|
+
# if 22 < i < 27 it is asc,mc,dsc,ic (angles of chart)
|
|
211
|
+
# put on special line (rplanet is range from outer ring)
|
|
212
|
+
amin, bmin, cmin = 0, 0, 0
|
|
213
|
+
if chart_type == "ExternalNatal":
|
|
214
|
+
amin = 74 - 10
|
|
215
|
+
bmin = 94 - 10
|
|
216
|
+
cmin = 40 - 10
|
|
217
|
+
|
|
218
|
+
if 22 < i < 27:
|
|
219
|
+
rplanet = 40 - cmin
|
|
220
|
+
elif switch == 1:
|
|
221
|
+
rplanet = 74 - amin
|
|
222
|
+
switch = 0
|
|
223
|
+
else:
|
|
224
|
+
rplanet = 94 - bmin
|
|
225
|
+
switch = 1
|
|
226
|
+
|
|
227
|
+
rtext = 45
|
|
228
|
+
|
|
229
|
+
offset = (int(main_subject_seventh_house_degree_ut) / -1) + int(points_deg_ut[i] + planets_delta[e])
|
|
230
|
+
trueoffset = (int(main_subject_seventh_house_degree_ut) / -1) + int(points_deg_ut[i])
|
|
231
|
+
|
|
232
|
+
planet_x = sliceToX(0, (radius - rplanet), offset) + rplanet
|
|
233
|
+
planet_y = sliceToY(0, (radius - rplanet), offset) + rplanet
|
|
234
|
+
if chart_type == "Transit" or chart_type == "Synastry":
|
|
235
|
+
scale = 0.8
|
|
236
|
+
|
|
237
|
+
elif chart_type == "ExternalNatal":
|
|
238
|
+
scale = 0.8
|
|
239
|
+
# line1
|
|
240
|
+
x1 = sliceToX(0, (radius - third_circle_radius), trueoffset) + third_circle_radius
|
|
241
|
+
y1 = sliceToY(0, (radius - third_circle_radius), trueoffset) + third_circle_radius
|
|
242
|
+
x2 = sliceToX(0, (radius - rplanet - 30), trueoffset) + rplanet + 30
|
|
243
|
+
y2 = sliceToY(0, (radius - rplanet - 30), trueoffset) + rplanet + 30
|
|
244
|
+
color = available_planets_setting[i]["color"]
|
|
245
|
+
output += (
|
|
246
|
+
'<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke-width:1px;stroke:%s;stroke-opacity:.3;"/>\n'
|
|
247
|
+
% (x1, y1, x2, y2, color)
|
|
248
|
+
)
|
|
249
|
+
# line2
|
|
250
|
+
x1 = sliceToX(0, (radius - rplanet - 30), trueoffset) + rplanet + 30
|
|
251
|
+
y1 = sliceToY(0, (radius - rplanet - 30), trueoffset) + rplanet + 30
|
|
252
|
+
x2 = sliceToX(0, (radius - rplanet - 10), offset) + rplanet + 10
|
|
253
|
+
y2 = sliceToY(0, (radius - rplanet - 10), offset) + rplanet + 10
|
|
254
|
+
output += (
|
|
255
|
+
'<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke-width:1px;stroke:%s;stroke-opacity:.5;"/>\n'
|
|
256
|
+
% (x1, y1, x2, y2, color)
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
else:
|
|
260
|
+
scale = 1
|
|
261
|
+
|
|
262
|
+
planet_details = available_kerykeion_celestial_points[i]
|
|
263
|
+
|
|
264
|
+
output += f'<g kr:node="ChartPoint" kr:house="{planet_details["house"]}" kr:sign="{planet_details["sign"]}" kr:slug="{planet_details["name"]}" transform="translate(-{12 * scale},-{12 * scale}) scale({scale})">'
|
|
265
|
+
output += f'<use x="{planet_x * (1/scale)}" y="{planet_y * (1/scale)}" xlink:href="#{available_planets_setting[i]["name"]}" />'
|
|
266
|
+
output += f"</g>"
|
|
267
|
+
|
|
268
|
+
# make transit degut and display planets
|
|
269
|
+
if chart_type == "Transit" or chart_type == "Synastry":
|
|
270
|
+
group_offset = {}
|
|
271
|
+
t_planets_degut = {}
|
|
272
|
+
list_range = len(available_planets_setting)
|
|
273
|
+
|
|
274
|
+
for i in range(list_range):
|
|
275
|
+
if chart_type == "Transit" and available_planets_setting[i]["name"] in TRANSIT_RING_EXCLUDE_POINTS_NAMES:
|
|
276
|
+
continue
|
|
277
|
+
|
|
278
|
+
group_offset[i] = 0
|
|
279
|
+
t_planets_degut[t_points_deg_ut[i]] = i
|
|
280
|
+
|
|
281
|
+
t_keys = list(t_planets_degut.keys())
|
|
282
|
+
t_keys.sort()
|
|
283
|
+
|
|
284
|
+
# grab closely grouped planets
|
|
285
|
+
groups = []
|
|
286
|
+
in_group = False
|
|
287
|
+
for e in range(len(t_keys)):
|
|
288
|
+
i_a = t_planets_degut[t_keys[e]]
|
|
289
|
+
if e == (len(t_keys) - 1):
|
|
290
|
+
i_b = t_planets_degut[t_keys[0]]
|
|
291
|
+
else:
|
|
292
|
+
i_b = t_planets_degut[t_keys[e + 1]]
|
|
293
|
+
|
|
294
|
+
a = t_points_deg_ut[i_a]
|
|
295
|
+
b = t_points_deg_ut[i_b]
|
|
296
|
+
diff = degreeDiff(a, b)
|
|
297
|
+
if diff <= 2.5:
|
|
298
|
+
if in_group:
|
|
299
|
+
groups[-1].append(i_b)
|
|
300
|
+
else:
|
|
301
|
+
groups.append([i_a])
|
|
302
|
+
groups[-1].append(i_b)
|
|
303
|
+
in_group = True
|
|
304
|
+
else:
|
|
305
|
+
in_group = False
|
|
306
|
+
# loop groups and set degrees display adjustment
|
|
307
|
+
for i in range(len(groups)):
|
|
308
|
+
if len(groups[i]) == 2:
|
|
309
|
+
group_offset[groups[i][0]] = -1.0
|
|
310
|
+
group_offset[groups[i][1]] = 1.0
|
|
311
|
+
elif len(groups[i]) == 3:
|
|
312
|
+
group_offset[groups[i][0]] = -1.5
|
|
313
|
+
group_offset[groups[i][1]] = 0
|
|
314
|
+
group_offset[groups[i][2]] = 1.5
|
|
315
|
+
elif len(groups[i]) == 4:
|
|
316
|
+
group_offset[groups[i][0]] = -2.0
|
|
317
|
+
group_offset[groups[i][1]] = -1.0
|
|
318
|
+
group_offset[groups[i][2]] = 1.0
|
|
319
|
+
group_offset[groups[i][3]] = 2.0
|
|
320
|
+
|
|
321
|
+
switch = 0
|
|
322
|
+
|
|
323
|
+
# Transit planets loop
|
|
324
|
+
for e in range(len(t_keys)):
|
|
325
|
+
if chart_type == "Transit" and available_planets_setting[e]["name"] in TRANSIT_RING_EXCLUDE_POINTS_NAMES:
|
|
326
|
+
continue
|
|
327
|
+
|
|
328
|
+
i = t_planets_degut[t_keys[e]]
|
|
329
|
+
|
|
330
|
+
if 22 < i < 27:
|
|
331
|
+
rplanet = 9
|
|
332
|
+
elif switch == 1:
|
|
333
|
+
rplanet = 18
|
|
334
|
+
switch = 0
|
|
335
|
+
else:
|
|
336
|
+
rplanet = 26
|
|
337
|
+
switch = 1
|
|
338
|
+
|
|
339
|
+
# Transit planet name
|
|
340
|
+
zeropoint = 360 - main_subject_seventh_house_degree_ut
|
|
341
|
+
t_offset = zeropoint + t_points_deg_ut[i]
|
|
342
|
+
if t_offset > 360:
|
|
343
|
+
t_offset = t_offset - 360
|
|
344
|
+
planet_x = sliceToX(0, (radius - rplanet), t_offset) + rplanet
|
|
345
|
+
planet_y = sliceToY(0, (radius - rplanet), t_offset) + rplanet
|
|
346
|
+
output += f'<g class="transit-planet-name" transform="translate(-6,-6)"><g transform="scale(0.5)"><use x="{planet_x*2}" y="{planet_y*2}" xlink:href="#{available_planets_setting[i]["name"]}" /></g></g>'
|
|
347
|
+
|
|
348
|
+
# Transit planet line
|
|
349
|
+
x1 = sliceToX(0, radius + 3, t_offset) - 3
|
|
350
|
+
y1 = sliceToY(0, radius + 3, t_offset) - 3
|
|
351
|
+
x2 = sliceToX(0, radius - 3, t_offset) + 3
|
|
352
|
+
y2 = sliceToY(0, radius - 3, t_offset) + 3
|
|
353
|
+
output += f'<line class="transit-planet-line" x1="{str(x1)}" y1="{str(y1)}" x2="{str(x2)}" y2="{str(y2)}" style="stroke: {available_planets_setting[i]["color"]}; stroke-width: 1px; stroke-opacity:.8;"/>'
|
|
354
|
+
|
|
355
|
+
# transit planet degree text
|
|
356
|
+
rotate = main_subject_first_house_degree_ut - t_points_deg_ut[i]
|
|
357
|
+
textanchor = "end"
|
|
358
|
+
t_offset += group_offset[i]
|
|
359
|
+
rtext = -3.0
|
|
360
|
+
|
|
361
|
+
if -90 > rotate > -270:
|
|
362
|
+
rotate = rotate + 180.0
|
|
363
|
+
textanchor = "start"
|
|
364
|
+
if 270 > rotate > 90:
|
|
365
|
+
rotate = rotate - 180.0
|
|
366
|
+
textanchor = "start"
|
|
367
|
+
|
|
368
|
+
if textanchor == "end":
|
|
369
|
+
xo = 1
|
|
370
|
+
else:
|
|
371
|
+
xo = -1
|
|
372
|
+
deg_x = sliceToX(0, (radius - rtext), t_offset + xo) + rtext
|
|
373
|
+
deg_y = sliceToY(0, (radius - rtext), t_offset + xo) + rtext
|
|
374
|
+
degree = int(t_offset)
|
|
375
|
+
output += f'<g transform="translate({deg_x},{deg_y})">'
|
|
376
|
+
output += f'<text transform="rotate({rotate})" text-anchor="{textanchor}'
|
|
377
|
+
output += f'" style="fill: {available_planets_setting[i]["color"]}; font-size: 10px;">{convert_decimal_to_degree_string(t_points_deg[i], type="1")}'
|
|
378
|
+
output += "</text></g>"
|
|
379
|
+
|
|
380
|
+
# check transit
|
|
381
|
+
if chart_type == "Transit" or chart_type == "Synastry":
|
|
382
|
+
dropin = 36
|
|
383
|
+
else:
|
|
384
|
+
dropin = 0
|
|
385
|
+
|
|
386
|
+
# planet line
|
|
387
|
+
x1 = sliceToX(0, radius - (dropin + 3), offset) + (dropin + 3)
|
|
388
|
+
y1 = sliceToY(0, radius - (dropin + 3), offset) + (dropin + 3)
|
|
389
|
+
x2 = sliceToX(0, (radius - (dropin - 3)), offset) + (dropin - 3)
|
|
390
|
+
y2 = sliceToY(0, (radius - (dropin - 3)), offset) + (dropin - 3)
|
|
391
|
+
|
|
392
|
+
output += f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke: {available_planets_setting[i]["color"]}; stroke-width: 2px; stroke-opacity:.6;"/>'
|
|
393
|
+
|
|
394
|
+
# check transit
|
|
395
|
+
if chart_type == "Transit" or chart_type == "Synastry":
|
|
396
|
+
dropin = 160
|
|
397
|
+
else:
|
|
398
|
+
dropin = 120
|
|
399
|
+
|
|
400
|
+
x1 = sliceToX(0, radius - dropin, offset) + dropin
|
|
401
|
+
y1 = sliceToY(0, radius - dropin, offset) + dropin
|
|
402
|
+
x2 = sliceToX(0, (radius - (dropin - 3)), offset) + (dropin - 3)
|
|
403
|
+
y2 = sliceToY(0, (radius - (dropin - 3)), offset) + (dropin - 3)
|
|
404
|
+
output += f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke: {available_planets_setting[i]["color"]}; stroke-width: 2px; stroke-opacity:.6;"/>'
|
|
405
|
+
|
|
406
|
+
return output
|