kerykeion 4.24.7__py3-none-any.whl → 4.25.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kerykeion might be problematic. Click here for more details.
- kerykeion/__init__.py +1 -0
- kerykeion/aspects/natal_aspects.py +2 -1
- kerykeion/astrological_subject.py +9 -47
- kerykeion/charts/charts_utils.py +40 -76
- kerykeion/charts/kerykeion_chart_svg.py +119 -59
- kerykeion/charts/templates/chart.xml +27 -13
- kerykeion/composite_subject_factory.py +200 -0
- kerykeion/kr_types/chart_types.py +20 -13
- kerykeion/kr_types/kr_literals.py +6 -1
- kerykeion/kr_types/kr_models.py +79 -2
- kerykeion/kr_types/settings_models.py +71 -20
- kerykeion/relationship_score/relationship_score_factory.py +7 -54
- kerykeion/settings/kr.config.json +545 -14
- kerykeion/utilities.py +73 -1
- {kerykeion-4.24.7.dist-info → kerykeion-4.25.1.dist-info}/METADATA +2 -2
- {kerykeion-4.24.7.dist-info → kerykeion-4.25.1.dist-info}/RECORD +19 -18
- {kerykeion-4.24.7.dist-info → kerykeion-4.25.1.dist-info}/LICENSE +0 -0
- {kerykeion-4.24.7.dist-info → kerykeion-4.25.1.dist-info}/WHEEL +0 -0
- {kerykeion-4.24.7.dist-info → kerykeion-4.25.1.dist-info}/entry_points.txt +0 -0
kerykeion/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from kerykeion import AstrologicalSubject
|
|
8
|
+
from kerykeion.kr_types import CompositeSubjectModel
|
|
8
9
|
import logging
|
|
9
10
|
from typing import Union, List
|
|
10
11
|
from kerykeion.settings.kerykeion_settings import get_settings
|
|
@@ -32,7 +33,7 @@ class NatalAspects:
|
|
|
32
33
|
Generates an object with all the aspects of a birthcart.
|
|
33
34
|
"""
|
|
34
35
|
|
|
35
|
-
user: Union[AstrologicalSubject, AstrologicalSubjectModel]
|
|
36
|
+
user: Union[AstrologicalSubject, AstrologicalSubjectModel, CompositeSubjectModel]
|
|
36
37
|
new_settings_file: Union[Path, KerykeionSettingsModel, dict, None] = None
|
|
37
38
|
active_points: List[Union[AxialCusps, Planet]] = field(default_factory=lambda: DEFAULT_ACTIVE_POINTS)
|
|
38
39
|
active_aspects: List[ActiveAspect] = field(default_factory=lambda: DEFAULT_ACTIVE_ASPECTS)
|
|
@@ -31,9 +31,8 @@ from kerykeion.utilities import (
|
|
|
31
31
|
get_number_from_name,
|
|
32
32
|
get_kerykeion_point_from_degree,
|
|
33
33
|
get_planet_house,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
check_and_adjust_polar_latitude
|
|
34
|
+
check_and_adjust_polar_latitude,
|
|
35
|
+
calculate_moon_phase
|
|
37
36
|
)
|
|
38
37
|
from pathlib import Path
|
|
39
38
|
from typing import Union, get_args
|
|
@@ -389,9 +388,15 @@ class AstrologicalSubject:
|
|
|
389
388
|
self.julian_day = float(swe.julday(utc_object.year, utc_object.month, utc_object.day, utc_float_hour_with_minutes))
|
|
390
389
|
# <--- UTC, julian day and local time setup
|
|
391
390
|
|
|
391
|
+
# Planets and Houses setup
|
|
392
392
|
self._initialize_houses()
|
|
393
393
|
self._initialize_planets()
|
|
394
|
-
|
|
394
|
+
|
|
395
|
+
# Lunar Phase
|
|
396
|
+
self.lunar_phase = calculate_moon_phase(
|
|
397
|
+
self.moon.abs_pos,
|
|
398
|
+
self.sun.abs_pos
|
|
399
|
+
)
|
|
395
400
|
|
|
396
401
|
# Deprecated properties
|
|
397
402
|
self.utc_time
|
|
@@ -667,49 +672,6 @@ class AstrologicalSubject:
|
|
|
667
672
|
self.medium_coeli.retrograde = False
|
|
668
673
|
self.imum_coeli.retrograde = False
|
|
669
674
|
|
|
670
|
-
def _initialize_moon_phase(self) -> None:
|
|
671
|
-
"""
|
|
672
|
-
Calculate and initialize the lunar phase based on the positions of the moon and sun.
|
|
673
|
-
|
|
674
|
-
This function calculates the degrees between the moon and the sun, determines the moon phase
|
|
675
|
-
and sun phase, and initializes the lunar phase model with the calculated values.
|
|
676
|
-
"""
|
|
677
|
-
# Initialize moon_phase and sun_phase to None in case of an error
|
|
678
|
-
moon_phase, sun_phase = None, None
|
|
679
|
-
|
|
680
|
-
# Calculate the anti-clockwise degrees between the sun and moon
|
|
681
|
-
moon, sun = self.moon.abs_pos, self.sun.abs_pos
|
|
682
|
-
degrees_between = (moon - sun) % 360
|
|
683
|
-
|
|
684
|
-
# Calculate the moon phase (1-28) based on the degrees between the sun and moon
|
|
685
|
-
step = 360.0 / 28.0
|
|
686
|
-
moon_phase = int(degrees_between // step) + 1
|
|
687
|
-
|
|
688
|
-
# Define the sun phase steps
|
|
689
|
-
sunstep = [
|
|
690
|
-
0, 30, 40, 50, 60, 70, 80, 90, 120, 130, 140, 150, 160, 170, 180,
|
|
691
|
-
210, 220, 230, 240, 250, 260, 270, 300, 310, 320, 330, 340, 350
|
|
692
|
-
]
|
|
693
|
-
|
|
694
|
-
# Calculate the sun phase (1-28) based on the degrees between the sun and moon
|
|
695
|
-
for x in range(len(sunstep)):
|
|
696
|
-
low = sunstep[x]
|
|
697
|
-
high = sunstep[x + 1] if x < len(sunstep) - 1 else 360
|
|
698
|
-
if low <= degrees_between < high:
|
|
699
|
-
sun_phase = x + 1
|
|
700
|
-
break
|
|
701
|
-
|
|
702
|
-
# Create a dictionary with the lunar phase information
|
|
703
|
-
lunar_phase_dictionary = {
|
|
704
|
-
"degrees_between_s_m": degrees_between,
|
|
705
|
-
"moon_phase": moon_phase,
|
|
706
|
-
"sun_phase": sun_phase,
|
|
707
|
-
"moon_emoji": get_moon_emoji_from_phase_int(moon_phase),
|
|
708
|
-
"moon_phase_name": get_moon_phase_name_from_phase_int(moon_phase)
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
# Initialize the lunar phase model with the calculated values
|
|
712
|
-
self.lunar_phase = LunarPhaseModel(**lunar_phase_dictionary)
|
|
713
675
|
|
|
714
676
|
def json(self, dump=False, destination_folder: Union[str, None] = None, indent: Union[int, None] = None) -> str:
|
|
715
677
|
"""
|
kerykeion/charts/charts_utils.py
CHANGED
|
@@ -28,6 +28,7 @@ def get_decoded_kerykeion_celestial_point_name(input_planet_name: str, celestial
|
|
|
28
28
|
else:
|
|
29
29
|
raise KerykeionException(f"Celestial point {input_planet_name} not found in language model.")
|
|
30
30
|
|
|
31
|
+
|
|
31
32
|
def decHourJoin(inH: int, inM: int, inS: int) -> float:
|
|
32
33
|
"""Join hour, minutes, seconds, timezone integer to hour float.
|
|
33
34
|
|
|
@@ -47,24 +48,42 @@ def decHourJoin(inH: int, inM: int, inS: int) -> float:
|
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
def degreeDiff(a: Union[int, float], b: Union[int, float]) -> float:
|
|
50
|
-
"""Calculate the difference between two degrees.
|
|
51
|
+
"""Calculate the smallest difference between two angles in degrees.
|
|
51
52
|
|
|
52
53
|
Args:
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
a (int | float): first angle in degrees
|
|
55
|
+
b (int | float): second angle in degrees
|
|
55
56
|
|
|
56
57
|
Returns:
|
|
57
|
-
float: difference between a and b
|
|
58
|
+
float: smallest difference between a and b (0 to 180 degrees)
|
|
58
59
|
"""
|
|
60
|
+
diff = math.fmod(abs(a - b), 360) # Assicura che il valore sia in [0, 360)
|
|
61
|
+
return min(diff, 360 - diff) # Prende l'angolo più piccolo tra i due possibili
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def degreeSum(a: Union[int, float], b: Union[int, float]) -> float:
|
|
65
|
+
"""Calculate the sum of two angles in degrees, normalized to [0, 360).
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
a (int | float): first angle in degrees
|
|
69
|
+
b (int | float): second angle in degrees
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
float: normalized sum of a and b in the range [0, 360)
|
|
73
|
+
"""
|
|
74
|
+
return math.fmod(a + b, 360) if (a + b) % 360 != 0 else 0.0
|
|
59
75
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
76
|
+
|
|
77
|
+
def normalizeDegree(angle: Union[int, float]) -> float:
|
|
78
|
+
"""Normalize an angle to the range [0, 360).
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
angle (int | float): The input angle in degrees.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
float: The normalized angle in the range [0, 360).
|
|
85
|
+
"""
|
|
86
|
+
return angle % 360 if angle % 360 != 0 else 0.0
|
|
68
87
|
|
|
69
88
|
|
|
70
89
|
def offsetToTz(datetime_offset: Union[datetime.timedelta, None]) -> float:
|
|
@@ -270,49 +289,6 @@ def draw_aspect_line(
|
|
|
270
289
|
f"</g>"
|
|
271
290
|
)
|
|
272
291
|
|
|
273
|
-
|
|
274
|
-
def draw_elements_percentages(
|
|
275
|
-
fire_label: str,
|
|
276
|
-
fire_points: float,
|
|
277
|
-
earth_label: str,
|
|
278
|
-
earth_points: float,
|
|
279
|
-
air_label: str,
|
|
280
|
-
air_points: float,
|
|
281
|
-
water_label: str,
|
|
282
|
-
water_points: float,
|
|
283
|
-
) -> str:
|
|
284
|
-
"""Draw the elements grid.
|
|
285
|
-
|
|
286
|
-
Args:
|
|
287
|
-
- fire_label (str): Label for fire
|
|
288
|
-
- fire_points (float): Points for fire
|
|
289
|
-
- earth_label (str): Label for earth
|
|
290
|
-
- earth_points (float): Points for earth
|
|
291
|
-
- air_label (str): Label for air
|
|
292
|
-
- air_points (float): Points for air
|
|
293
|
-
- water_label (str): Label for water
|
|
294
|
-
- water_points (float): Points for water
|
|
295
|
-
|
|
296
|
-
Returns:
|
|
297
|
-
str: The SVG elements grid as a string.
|
|
298
|
-
"""
|
|
299
|
-
total = fire_points + earth_points + air_points + water_points
|
|
300
|
-
|
|
301
|
-
fire_percentage = int(round(100 * fire_points / total))
|
|
302
|
-
earth_percentage = int(round(100 * earth_points / total))
|
|
303
|
-
air_percentage = int(round(100 * air_points / total))
|
|
304
|
-
water_percentage = int(round(100 * water_points / total))
|
|
305
|
-
|
|
306
|
-
return (
|
|
307
|
-
f'<g transform="translate(-30,79)">'
|
|
308
|
-
f'<text y="0" style="fill: var(--kerykeion-chart-color-fire-percentage); font-size: 10px;">{fire_label} {str(fire_percentage)}%</text>'
|
|
309
|
-
f'<text y="12" style="fill: var(--kerykeion-chart-color-earth-percentage); font-size: 10px;">{earth_label} {str(earth_percentage)}%</text>'
|
|
310
|
-
f'<text y="24" style="fill: var(--kerykeion-chart-color-air-percentage); font-size: 10px;">{air_label} {str(air_percentage)}%</text>'
|
|
311
|
-
f'<text y="36" style="fill: var(--kerykeion-chart-color-water-percentage); font-size: 10px;">{water_label} {str(water_percentage)}%</text>'
|
|
312
|
-
f"</g>"
|
|
313
|
-
)
|
|
314
|
-
|
|
315
|
-
|
|
316
292
|
def convert_decimal_to_degree_string(dec: float, format_type: Literal["1", "2", "3"] = "3") -> str:
|
|
317
293
|
"""
|
|
318
294
|
Converts a decimal float to a degrees string in the specified format.
|
|
@@ -766,22 +742,19 @@ def draw_transit_aspect_list(
|
|
|
766
742
|
return out
|
|
767
743
|
|
|
768
744
|
|
|
769
|
-
def
|
|
745
|
+
def calculate_moon_phase_chart_params(
|
|
770
746
|
degrees_between_sun_and_moon: float,
|
|
771
747
|
latitude: float
|
|
772
|
-
) ->
|
|
748
|
+
) -> dict:
|
|
773
749
|
"""
|
|
774
|
-
|
|
750
|
+
Calculate the parameters for the moon phase chart.
|
|
775
751
|
|
|
776
752
|
Parameters:
|
|
777
753
|
- degrees_between_sun_and_moon (float): The degrees between the sun and the moon.
|
|
778
754
|
- latitude (float): The latitude for rotation calculation.
|
|
779
|
-
- lunar_phase_outline_color (str): The color for the lunar phase outline.
|
|
780
|
-
- dark_color (str): The color for the dark part of the moon.
|
|
781
|
-
- light_color (str): The color for the light part of the moon.
|
|
782
755
|
|
|
783
756
|
Returns:
|
|
784
|
-
-
|
|
757
|
+
- dict: The moon phase chart parameters.
|
|
785
758
|
"""
|
|
786
759
|
deg = degrees_between_sun_and_moon
|
|
787
760
|
|
|
@@ -825,20 +798,11 @@ def draw_moon_phase(
|
|
|
825
798
|
# Calculate rotation based on latitude
|
|
826
799
|
lunar_phase_rotate = -90.0 - latitude
|
|
827
800
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
f' <circle cx="20" cy="10" r="10" />'
|
|
834
|
-
f' </clipPath>'
|
|
835
|
-
f' </defs>'
|
|
836
|
-
f' <circle cx="20" cy="10" r="10" style="fill: var(--kerykeion-chart-color-lunar-phase-0)" />'
|
|
837
|
-
f' <circle cx="{circle_center_x}" cy="10" r="{circle_radius}" style="fill: var(--kerykeion-chart-color-lunar-phase-1)" clip-path="url(#moonPhaseCutOffCircle)" />'
|
|
838
|
-
f' <circle cx="20" cy="10" r="10" style="fill: none; stroke: var(--kerykeion-chart-color-lunar-phase-0); stroke-width: 0.5px; stroke-opacity: 0.5" />'
|
|
839
|
-
f'</g>'
|
|
840
|
-
)
|
|
841
|
-
|
|
801
|
+
return {
|
|
802
|
+
"circle_center_x": circle_center_x,
|
|
803
|
+
"circle_radius": circle_radius,
|
|
804
|
+
"lunar_phase_rotate": lunar_phase_rotate,
|
|
805
|
+
}
|
|
842
806
|
|
|
843
807
|
def draw_house_grid(
|
|
844
808
|
main_subject_houses_list: list[KerykeionPointModel],
|
|
@@ -14,7 +14,7 @@ from kerykeion.aspects.natal_aspects import NatalAspects
|
|
|
14
14
|
from kerykeion.astrological_subject import AstrologicalSubject
|
|
15
15
|
from kerykeion.kr_types import KerykeionException, ChartType, KerykeionPointModel, Sign, ActiveAspect
|
|
16
16
|
from kerykeion.kr_types import ChartTemplateDictionary
|
|
17
|
-
from kerykeion.kr_types.kr_models import AstrologicalSubjectModel
|
|
17
|
+
from kerykeion.kr_types.kr_models import AstrologicalSubjectModel, CompositeSubjectModel
|
|
18
18
|
from kerykeion.kr_types.settings_models import KerykeionSettingsCelestialPointModel, KerykeionSettingsModel
|
|
19
19
|
from kerykeion.kr_types.kr_literals import KerykeionChartTheme, KerykeionChartLanguage, AxialCusps, Planet
|
|
20
20
|
from kerykeion.charts.charts_utils import (
|
|
@@ -22,7 +22,6 @@ from kerykeion.charts.charts_utils import (
|
|
|
22
22
|
convert_latitude_coordinate_to_string,
|
|
23
23
|
convert_longitude_coordinate_to_string,
|
|
24
24
|
draw_aspect_line,
|
|
25
|
-
draw_elements_percentages,
|
|
26
25
|
draw_transit_ring_degree_steps,
|
|
27
26
|
draw_degree_ring,
|
|
28
27
|
draw_transit_ring,
|
|
@@ -33,7 +32,7 @@ from kerykeion.charts.charts_utils import (
|
|
|
33
32
|
draw_houses_cusps_and_text_number,
|
|
34
33
|
draw_transit_aspect_list,
|
|
35
34
|
draw_transit_aspect_grid,
|
|
36
|
-
|
|
35
|
+
calculate_moon_phase_chart_params,
|
|
37
36
|
draw_house_grid,
|
|
38
37
|
draw_planet_grid,
|
|
39
38
|
)
|
|
@@ -113,7 +112,7 @@ class KerykeionChartSVG:
|
|
|
113
112
|
chart_colors_settings: dict
|
|
114
113
|
planets_settings: dict
|
|
115
114
|
aspects_settings: dict
|
|
116
|
-
user: Union[AstrologicalSubject, AstrologicalSubjectModel]
|
|
115
|
+
user: Union[AstrologicalSubject, AstrologicalSubjectModel, CompositeSubjectModel]
|
|
117
116
|
available_planets_setting: List[KerykeionSettingsCelestialPointModel]
|
|
118
117
|
height: float
|
|
119
118
|
location: str
|
|
@@ -123,7 +122,7 @@ class KerykeionChartSVG:
|
|
|
123
122
|
|
|
124
123
|
def __init__(
|
|
125
124
|
self,
|
|
126
|
-
first_obj: Union[AstrologicalSubject, AstrologicalSubjectModel],
|
|
125
|
+
first_obj: Union[AstrologicalSubject, AstrologicalSubjectModel, CompositeSubjectModel],
|
|
127
126
|
chart_type: ChartType = "Natal",
|
|
128
127
|
second_obj: Union[AstrologicalSubject, AstrologicalSubjectModel, None] = None,
|
|
129
128
|
new_output_directory: Union[str, None] = None,
|
|
@@ -178,7 +177,7 @@ class KerykeionChartSVG:
|
|
|
178
177
|
)
|
|
179
178
|
self.aspects_list = natal_aspects_instance.relevant_aspects
|
|
180
179
|
|
|
181
|
-
|
|
180
|
+
elif self.chart_type == "Transit" or self.chart_type == "Synastry":
|
|
182
181
|
if not second_obj:
|
|
183
182
|
raise KerykeionException("Second object is required for Transit or Synastry charts.")
|
|
184
183
|
|
|
@@ -198,6 +197,12 @@ class KerykeionChartSVG:
|
|
|
198
197
|
for body in available_celestial_points_names:
|
|
199
198
|
self.t_available_kerykeion_celestial_points.append(self.t_user.get(body))
|
|
200
199
|
|
|
200
|
+
elif self.chart_type == "Composite":
|
|
201
|
+
if not isinstance(first_obj, CompositeSubjectModel):
|
|
202
|
+
raise KerykeionException("First object must be a CompositeSubjectModel instance.")
|
|
203
|
+
|
|
204
|
+
self.aspects_list = NatalAspects(self.user, new_settings_file=self.new_settings_file, active_points=active_points).relevant_aspects
|
|
205
|
+
|
|
201
206
|
# Double chart aspect grid type
|
|
202
207
|
self.double_chart_aspect_grid_type = double_chart_aspect_grid_type
|
|
203
208
|
|
|
@@ -210,12 +215,20 @@ class KerykeionChartSVG:
|
|
|
210
215
|
else:
|
|
211
216
|
self.width = self._DEFAULT_NATAL_WIDTH
|
|
212
217
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
218
|
+
if self.chart_type in ["Natal", "ExternalNatal", "Synastry"]:
|
|
219
|
+
self.location = self.user.city
|
|
220
|
+
self.geolat = self.user.lat
|
|
221
|
+
self.geolon = self.user.lng
|
|
222
|
+
|
|
223
|
+
elif self.chart_type == "Composite":
|
|
224
|
+
self.location = ""
|
|
225
|
+
self.geolat = (self.user.first_subject.lat + self.user.second_subject.lat) / 2
|
|
226
|
+
self.geolon = (self.user.first_subject.lng + self.user.second_subject.lng) / 2
|
|
227
|
+
|
|
228
|
+
elif self.chart_type in ["Transit"]:
|
|
229
|
+
self.location = self.t_user.city
|
|
230
|
+
self.geolat = self.t_user.lat
|
|
231
|
+
self.geolon = self.t_user.lng
|
|
219
232
|
self.t_name = self.language_settings["transit_name"]
|
|
220
233
|
|
|
221
234
|
# Default radius for the chart
|
|
@@ -393,7 +406,7 @@ class KerykeionChartSVG:
|
|
|
393
406
|
ChartTemplateDictionary: A dictionary with template data for the chart.
|
|
394
407
|
"""
|
|
395
408
|
# Initialize template dictionary
|
|
396
|
-
template_dict:
|
|
409
|
+
template_dict: dict = {}
|
|
397
410
|
|
|
398
411
|
# Set the color style tag
|
|
399
412
|
template_dict["color_style_tag"] = self.color_style_tag
|
|
@@ -402,11 +415,8 @@ class KerykeionChartSVG:
|
|
|
402
415
|
template_dict["chart_height"] = self.height
|
|
403
416
|
template_dict["chart_width"] = self.width
|
|
404
417
|
|
|
405
|
-
# Set chart name
|
|
406
|
-
template_dict["stringName"] = f"{self.user.name}:" if self.chart_type in ["Synastry", "Transit"] else f'{self.language_settings["info"]}:'
|
|
407
|
-
|
|
408
418
|
# Set viewbox based on chart type
|
|
409
|
-
if self.chart_type in ["Natal", "ExternalNatal"]:
|
|
419
|
+
if self.chart_type in ["Natal", "ExternalNatal", "Composite"]:
|
|
410
420
|
template_dict['viewbox'] = self._BASIC_CHART_VIEWBOX
|
|
411
421
|
elif self.double_chart_aspect_grid_type == "table" and self.chart_type == "Transit":
|
|
412
422
|
template_dict['viewbox'] = self._TRANSIT_CHART_WITH_TABLE_VIWBOX
|
|
@@ -442,58 +452,85 @@ class KerykeionChartSVG:
|
|
|
442
452
|
template_dict["stringTitle"] = f"{self.user.name} {self.language_settings['and_word']} {self.t_user.name}"
|
|
443
453
|
elif self.chart_type == "Transit":
|
|
444
454
|
template_dict["stringTitle"] = f"{self.language_settings['transits']} {self.t_user.day}/{self.t_user.month}/{self.t_user.year}"
|
|
445
|
-
|
|
455
|
+
elif self.chart_type in ["Natal", "ExternalNatal"]:
|
|
446
456
|
template_dict["stringTitle"] = self.user.name
|
|
457
|
+
elif self.chart_type == "Composite":
|
|
458
|
+
template_dict["stringTitle"] = f"{self.user.first_subject.name} {self.language_settings['and_word']} {self.user.second_subject.name}"
|
|
447
459
|
|
|
460
|
+
# Zodiac Type Info
|
|
448
461
|
if self.user.zodiac_type == 'Tropic':
|
|
449
|
-
zodiac_info = "
|
|
450
|
-
|
|
462
|
+
zodiac_info = f"{self.language_settings.get('zodiac', 'Zodiac')}: {self.language_settings.get('tropical', 'Tropical')}"
|
|
451
463
|
else:
|
|
452
464
|
mode_const = "SIDM_" + self.user.sidereal_mode # type: ignore
|
|
453
465
|
mode_name = swe.get_ayanamsa_name(getattr(swe, mode_const))
|
|
454
|
-
zodiac_info = f"Ayanamsa: {mode_name}"
|
|
466
|
+
zodiac_info = f"{self.language_settings.get('ayanamsa', 'Ayanamsa')}: {mode_name}"
|
|
455
467
|
|
|
456
|
-
template_dict["
|
|
457
|
-
template_dict["
|
|
468
|
+
template_dict["bottom_left_0"] = f"{self.language_settings.get('houses_system_' + self.user.houses_system_identifier, self.user.houses_system_name)} {self.language_settings.get('houses', 'Houses')}"
|
|
469
|
+
template_dict["bottom_left_1"] = zodiac_info
|
|
458
470
|
|
|
459
471
|
if self.chart_type in ["Natal", "ExternalNatal", "Synastry"]:
|
|
460
|
-
template_dict["
|
|
461
|
-
template_dict["
|
|
462
|
-
template_dict["
|
|
463
|
-
|
|
464
|
-
template_dict["
|
|
465
|
-
template_dict["
|
|
466
|
-
template_dict["
|
|
472
|
+
template_dict["bottom_left_2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")} {self.language_settings.get("day", "Day").lower()}: {self.user.lunar_phase.get("moon_phase", "")}'
|
|
473
|
+
template_dict["bottom_left_3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get(self.user.lunar_phase.moon_phase_name.lower().replace(" ", "_"), self.user.lunar_phase.moon_phase_name)}'
|
|
474
|
+
template_dict["bottom_left_4"] = f'{self.language_settings.get(self.user.perspective_type.lower().replace(" ", "_"), self.user.perspective_type)}'
|
|
475
|
+
elif self.chart_type == "Transit":
|
|
476
|
+
template_dict["bottom_left_2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get("day", "Day")} {self.t_user.lunar_phase.get("moon_phase", "")}'
|
|
477
|
+
template_dict["bottom_left_3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.t_user.lunar_phase.moon_phase_name}'
|
|
478
|
+
template_dict["bottom_left_4"] = f'{self.language_settings.get(self.t_user.perspective_type.lower().replace(" ", "_"), self.t_user.perspective_type)}'
|
|
479
|
+
elif self.chart_type == "Composite":
|
|
480
|
+
template_dict["bottom_left_2"] = f'{self.user.first_subject.perspective_type}'
|
|
481
|
+
template_dict["bottom_left_3"] = f'{self.language_settings.get("composite_chart", "Composite Chart")} - {self.language_settings.get("midpoints", "Midpoints")}'
|
|
482
|
+
template_dict["bottom_left_4"] = ""
|
|
467
483
|
|
|
468
484
|
# Draw moon phase
|
|
469
|
-
|
|
485
|
+
moon_phase_dict = calculate_moon_phase_chart_params(
|
|
470
486
|
self.user.lunar_phase["degrees_between_s_m"],
|
|
471
487
|
self.geolat
|
|
472
488
|
)
|
|
473
489
|
|
|
490
|
+
template_dict["lunar_phase_rotate"] = moon_phase_dict["lunar_phase_rotate"]
|
|
491
|
+
template_dict["lunar_phase_circle_center_x"] = moon_phase_dict["circle_center_x"]
|
|
492
|
+
template_dict["lunar_phase_circle_radius"] = moon_phase_dict["circle_radius"]
|
|
493
|
+
|
|
494
|
+
if self.chart_type == "Composite":
|
|
495
|
+
template_dict["top_left_1"] = f"{datetime.fromisoformat(self.user.first_subject.iso_formatted_local_datetime).strftime('%Y-%m-%d %H:%M')}"
|
|
474
496
|
# Set location string
|
|
475
|
-
|
|
497
|
+
elif len(self.location) > 35:
|
|
476
498
|
split_location = self.location.split(",")
|
|
477
499
|
if len(split_location) > 1:
|
|
478
|
-
template_dict["
|
|
479
|
-
if len(template_dict["
|
|
480
|
-
template_dict["
|
|
500
|
+
template_dict["top_left_1"] = split_location[0] + ", " + split_location[-1]
|
|
501
|
+
if len(template_dict["top_left_1"]) > 35:
|
|
502
|
+
template_dict["top_left_1"] = template_dict["top_left_1"][:35] + "..."
|
|
481
503
|
else:
|
|
482
|
-
template_dict["
|
|
504
|
+
template_dict["top_left_1"] = self.location[:35] + "..."
|
|
483
505
|
else:
|
|
484
|
-
template_dict["
|
|
506
|
+
template_dict["top_left_1"] = self.location
|
|
507
|
+
|
|
508
|
+
# Set chart name
|
|
509
|
+
if self.chart_type in ["Synastry", "Transit"]:
|
|
510
|
+
template_dict["top_left_0"] = f"{self.user.name}:"
|
|
511
|
+
elif self.chart_type in ["Natal", "ExternalNatal"]:
|
|
512
|
+
template_dict["top_left_0"] = f'{self.language_settings["info"]}:'
|
|
513
|
+
elif self.chart_type == "Composite":
|
|
514
|
+
template_dict["top_left_0"] = f'{self.user.first_subject.name}'
|
|
485
515
|
|
|
486
516
|
# Set additional information for Synastry chart type
|
|
487
517
|
if self.chart_type == "Synastry":
|
|
488
|
-
template_dict["
|
|
489
|
-
template_dict["
|
|
490
|
-
template_dict["
|
|
518
|
+
template_dict["top_left_3"] = f"{self.t_user.name}: "
|
|
519
|
+
template_dict["top_left_4"] = self.t_user.city
|
|
520
|
+
template_dict["top_left_5"] = f"{self.t_user.year}-{self.t_user.month}-{self.t_user.day} {self.t_user.hour:02d}:{self.t_user.minute:02d}"
|
|
521
|
+
elif self.chart_type == "Composite":
|
|
522
|
+
template_dict["top_left_3"] = self.user.second_subject.name
|
|
523
|
+
template_dict["top_left_4"] = f"{datetime.fromisoformat(self.user.second_subject.iso_formatted_local_datetime).strftime('%Y-%m-%d %H:%M')}"
|
|
524
|
+
latitude_string = convert_latitude_coordinate_to_string(self.user.second_subject.lat, self.language_settings['north_letter'], self.language_settings['south_letter'])
|
|
525
|
+
longitude_string = convert_longitude_coordinate_to_string(self.user.second_subject.lng, self.language_settings['east_letter'], self.language_settings['west_letter'])
|
|
526
|
+
template_dict["top_left_5"] = f"{latitude_string} / {longitude_string}"
|
|
491
527
|
else:
|
|
492
528
|
latitude_string = convert_latitude_coordinate_to_string(self.geolat, self.language_settings['north'], self.language_settings['south'])
|
|
493
529
|
longitude_string = convert_longitude_coordinate_to_string(self.geolon, self.language_settings['east'], self.language_settings['west'])
|
|
494
|
-
template_dict["
|
|
495
|
-
template_dict["
|
|
496
|
-
template_dict["
|
|
530
|
+
template_dict["top_left_3"] = f"{self.language_settings['latitude']}: {latitude_string}"
|
|
531
|
+
template_dict["top_left_4"] = f"{self.language_settings['longitude']}: {longitude_string}"
|
|
532
|
+
template_dict["top_left_5"] = f"{self.language_settings['type']}: {self.language_settings.get(self.chart_type, self.chart_type)}"
|
|
533
|
+
|
|
497
534
|
|
|
498
535
|
# Set paper colors
|
|
499
536
|
template_dict["paper_color_0"] = self.chart_colors_settings["paper_0"]
|
|
@@ -589,16 +626,17 @@ class KerykeionChartSVG:
|
|
|
589
626
|
)
|
|
590
627
|
|
|
591
628
|
# Draw elements percentages
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
629
|
+
total = self.fire + self.water + self.earth + self.air
|
|
630
|
+
|
|
631
|
+
fire_percentage = int(round(100 * self.fire / total))
|
|
632
|
+
earth_percentage = int(round(100 * self.earth / total))
|
|
633
|
+
air_percentage = int(round(100 * self.air / total))
|
|
634
|
+
water_percentage = int(round(100 * self.water / total))
|
|
635
|
+
|
|
636
|
+
template_dict["fire_string"] = f"{self.language_settings['fire']} {fire_percentage}%"
|
|
637
|
+
template_dict["earth_string"] = f"{self.language_settings['earth']} {earth_percentage}%"
|
|
638
|
+
template_dict["air_string"] = f"{self.language_settings['air']} {air_percentage}%"
|
|
639
|
+
template_dict["water_string"] = f"{self.language_settings['water']} {water_percentage}%"
|
|
602
640
|
|
|
603
641
|
# Draw planet grid
|
|
604
642
|
if self.chart_type in ["Transit", "Synastry"]:
|
|
@@ -606,6 +644,7 @@ class KerykeionChartSVG:
|
|
|
606
644
|
second_subject_table_name = self.language_settings["transit_name"]
|
|
607
645
|
else:
|
|
608
646
|
second_subject_table_name = self.t_user.name
|
|
647
|
+
|
|
609
648
|
template_dict["makePlanetGrid"] = draw_planet_grid(
|
|
610
649
|
planets_and_houses_grid_title=self.language_settings["planets_and_house"],
|
|
611
650
|
subject_name=self.user.name,
|
|
@@ -617,9 +656,14 @@ class KerykeionChartSVG:
|
|
|
617
656
|
second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
|
|
618
657
|
)
|
|
619
658
|
else:
|
|
659
|
+
if self.chart_type == "Composite":
|
|
660
|
+
subject_name = f"{self.user.first_subject.name} {self.language_settings['and_word']} {self.user.second_subject.name}"
|
|
661
|
+
else:
|
|
662
|
+
subject_name = self.user.name
|
|
663
|
+
|
|
620
664
|
template_dict["makePlanetGrid"] = draw_planet_grid(
|
|
621
665
|
planets_and_houses_grid_title=self.language_settings["planets_and_house"],
|
|
622
|
-
subject_name=
|
|
666
|
+
subject_name=subject_name,
|
|
623
667
|
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
624
668
|
chart_type=self.chart_type,
|
|
625
669
|
text_color=self.chart_colors_settings["paper_0"],
|
|
@@ -627,12 +671,18 @@ class KerykeionChartSVG:
|
|
|
627
671
|
)
|
|
628
672
|
|
|
629
673
|
# Set date time string
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
674
|
+
if self.chart_type in ["Composite"]:
|
|
675
|
+
# First Subject Latitude and Longitude
|
|
676
|
+
latitude = convert_latitude_coordinate_to_string(self.user.first_subject.lat, self.language_settings["north_letter"], self.language_settings["south_letter"])
|
|
677
|
+
longitude = convert_longitude_coordinate_to_string(self.user.first_subject.lng, self.language_settings["east_letter"], self.language_settings["west_letter"])
|
|
678
|
+
template_dict["top_left_2"] = f"{latitude} {longitude}"
|
|
679
|
+
else:
|
|
680
|
+
dt = datetime.fromisoformat(self.user.iso_formatted_local_datetime)
|
|
681
|
+
custom_format = dt.strftime('%Y-%m-%d %H:%M [%z]')
|
|
682
|
+
custom_format = custom_format[:-3] + ':' + custom_format[-3:]
|
|
683
|
+
template_dict["top_left_2"] = f"{custom_format}"
|
|
634
684
|
|
|
635
|
-
return template_dict
|
|
685
|
+
return ChartTemplateDictionary(**template_dict)
|
|
636
686
|
|
|
637
687
|
def makeTemplate(self, minify: bool = False) -> str:
|
|
638
688
|
"""Creates the template for the SVG file"""
|
|
@@ -734,6 +784,7 @@ class KerykeionChartSVG:
|
|
|
734
784
|
|
|
735
785
|
if __name__ == "__main__":
|
|
736
786
|
from kerykeion.utilities import setup_logging
|
|
787
|
+
from kerykeion.composite_subject_factory import CompositeSubjectFactory
|
|
737
788
|
setup_logging(level="debug")
|
|
738
789
|
|
|
739
790
|
first = AstrologicalSubject("John Lennon", 1940, 10, 9, 18, 30, "Liverpool", "GB")
|
|
@@ -952,3 +1003,12 @@ if __name__ == "__main__":
|
|
|
952
1003
|
kanye_west_subject = AstrologicalSubject("Kanye", 1977, 6, 8, 8, 45, "Atlanta", "US")
|
|
953
1004
|
kanye_west_chart = KerykeionChartSVG(kanye_west_subject)
|
|
954
1005
|
kanye_west_chart.makeSVG()
|
|
1006
|
+
|
|
1007
|
+
# Composite Chart
|
|
1008
|
+
angelina = AstrologicalSubject("Angelina Jolie", 1975, 6, 4, 9, 9, "Los Angeles", "US", lng=-118.15, lat=34.03, tz_str="America/Los_Angeles")
|
|
1009
|
+
brad = AstrologicalSubject("Brad Pitt", 1963, 12, 18, 6, 31, "Shawnee", "US", lng=-96.56, lat=35.20, tz_str="America/Chicago")
|
|
1010
|
+
|
|
1011
|
+
composite_subject_factory = CompositeSubjectFactory(angelina, brad)
|
|
1012
|
+
composite_subject_model = composite_subject_factory.get_midpoint_composite_subject_model()
|
|
1013
|
+
composite_chart = KerykeionChartSVG(composite_subject_model, "Composite")
|
|
1014
|
+
composite_chart.makeSVG()
|
|
@@ -26,22 +26,31 @@ OpenAstro.org -->
|
|
|
26
26
|
<rect class="background-rectangle" x="0" y="0" width="$chart_width"
|
|
27
27
|
height="$chart_height" style="fill: $paper_color_1" />
|
|
28
28
|
<text x="20" y="22" style="fill: $paper_color_0; font-size: 24px">$stringTitle</text>
|
|
29
|
-
<text x="20" y="50" style="fill: $paper_color_0; font-size:
|
|
30
|
-
<text x="20" y="62" style="fill: $paper_color_0; font-size:
|
|
31
|
-
<text x="20" y="74" style="fill: $paper_color_0; font-size:
|
|
32
|
-
<text x="20" y="86" style="fill: $paper_color_0; font-size:
|
|
33
|
-
<text x="20" y="98" style="fill: $paper_color_0; font-size:
|
|
34
|
-
<text x="20" y="110" style="fill: $paper_color_0; font-size:
|
|
35
|
-
<text x="20" y="452" style="fill: $paper_color_0; font-size: 10px">$
|
|
36
|
-
<text x="20" y="466" style="fill: $paper_color_0; font-size: 10px">$
|
|
37
|
-
<text x="20" y="480" style="fill: $paper_color_0; font-size: 10px">$
|
|
38
|
-
<text x="20" y="494" style="fill: $paper_color_0; font-size: 10px">$
|
|
39
|
-
<text x="20" y="508" style="fill: $paper_color_0; font-size: 10px">$
|
|
29
|
+
<text x="20" y="50" style="fill: $paper_color_0; font-size: 10px">$top_left_0</text>
|
|
30
|
+
<text x="20" y="62" style="fill: $paper_color_0; font-size: 10px">$top_left_1</text>
|
|
31
|
+
<text x="20" y="74" style="fill: $paper_color_0; font-size: 10px">$top_left_2</text>
|
|
32
|
+
<text x="20" y="86" style="fill: $paper_color_0; font-size: 10px">$top_left_3</text>
|
|
33
|
+
<text x="20" y="98" style="fill: $paper_color_0; font-size: 10px">$top_left_4</text>
|
|
34
|
+
<text x="20" y="110" style="fill: $paper_color_0; font-size: 10px">$top_left_5</text>
|
|
35
|
+
<text x="20" y="452" style="fill: $paper_color_0; font-size: 10px">$bottom_left_0</text>
|
|
36
|
+
<text x="20" y="466" style="fill: $paper_color_0; font-size: 10px">$bottom_left_1</text>
|
|
37
|
+
<text x="20" y="480" style="fill: $paper_color_0; font-size: 10px">$bottom_left_2</text>
|
|
38
|
+
<text x="20" y="494" style="fill: $paper_color_0; font-size: 10px">$bottom_left_3</text>
|
|
39
|
+
<text x="20" y="508" style="fill: $paper_color_0; font-size: 10px">$bottom_left_4</text>
|
|
40
40
|
</g>
|
|
41
41
|
|
|
42
42
|
<!-- Lunar Phase -->
|
|
43
43
|
<g kr:node="Lunar_Phase" transform="translate(20,518)">
|
|
44
|
-
$
|
|
44
|
+
<g transform="rotate($lunar_phase_rotate 20 10)">
|
|
45
|
+
<defs>
|
|
46
|
+
<clipPath id="moonPhaseCutOffCircle">
|
|
47
|
+
<circle cx="20" cy="10" r="10" />
|
|
48
|
+
</clipPath>
|
|
49
|
+
</defs>
|
|
50
|
+
<circle cx="20" cy="10" r="10" style="fill: var(--kerykeion-chart-color-lunar-phase-0)" />
|
|
51
|
+
<circle cx="$lunar_phase_circle_center_x" cy="10" r="$lunar_phase_circle_radius" style="fill: var(--kerykeion-chart-color-lunar-phase-1)" clip-path="url(#moonPhaseCutOffCircle)" />
|
|
52
|
+
<circle cx="20" cy="10" r="10" style="fill: none; stroke: var(--kerykeion-chart-color-lunar-phase-0); stroke-width: 0.5px; stroke-opacity: 0.5" />
|
|
53
|
+
</g>
|
|
45
54
|
</g>
|
|
46
55
|
|
|
47
56
|
<g kr:node="Main_Content" transform="translate(50,50)">
|
|
@@ -100,7 +109,12 @@ OpenAstro.org -->
|
|
|
100
109
|
|
|
101
110
|
<!-- Elements -->
|
|
102
111
|
<g kr:node="Elements_Percentages">
|
|
103
|
-
|
|
112
|
+
<g transform="translate(-30,79)">
|
|
113
|
+
<text y="0" style="fill: var(--kerykeion-chart-color-fire-percentage); font-size: 10px;">$fire_string</text>
|
|
114
|
+
<text y="12" style="fill: var(--kerykeion-chart-color-earth-percentage); font-size: 10px;">$earth_string</text>
|
|
115
|
+
<text y="24" style="fill: var(--kerykeion-chart-color-air-percentage); font-size: 10px;">$air_string</text>
|
|
116
|
+
<text y="36" style="fill: var(--kerykeion-chart-color-water-percentage); font-size: 10px;">$water_string</text>
|
|
117
|
+
</g>"
|
|
104
118
|
</g>
|
|
105
119
|
|
|
106
120
|
<!-- Houses_And_Planets_Grid -->
|