kerykeion 5.1.9__py3-none-any.whl → 5.1.12__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/aspects/aspects_factory.py +37 -13
- kerykeion/aspects/aspects_utils.py +159 -52
- kerykeion/astrological_subject_factory.py +75 -56
- kerykeion/charts/chart_drawer.py +151 -106
- kerykeion/charts/templates/chart.xml +22 -22
- kerykeion/report.py +0 -1
- kerykeion/schemas/kr_literals.py +8 -2
- kerykeion/schemas/kr_models.py +2 -2
- {kerykeion-5.1.9.dist-info → kerykeion-5.1.12.dist-info}/METADATA +8 -392
- {kerykeion-5.1.9.dist-info → kerykeion-5.1.12.dist-info}/RECORD +12 -12
- {kerykeion-5.1.9.dist-info → kerykeion-5.1.12.dist-info}/WHEEL +0 -0
- {kerykeion-5.1.9.dist-info → kerykeion-5.1.12.dist-info}/licenses/LICENSE +0 -0
|
@@ -330,12 +330,25 @@ class AspectsFactory:
|
|
|
330
330
|
first_planet_id = planet_id_lookup.get(first_name, 0)
|
|
331
331
|
second_planet_id = planet_id_lookup.get(second_name, 0)
|
|
332
332
|
|
|
333
|
-
#
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
333
|
+
# Determine aspect movement.
|
|
334
|
+
# If both points are chart axes, there is no meaningful
|
|
335
|
+
# dynamic movement between them, so we mark the aspect as
|
|
336
|
+
# "Fixed" regardless of any synthetic speeds.
|
|
337
|
+
if first_name in AXES_LIST and second_name in AXES_LIST:
|
|
338
|
+
aspect_movement = "Fixed"
|
|
339
|
+
else:
|
|
340
|
+
# Get speeds, fall back to 0.0 only if missing/None
|
|
341
|
+
first_speed = active_points_list[first].get("speed") or 0.0
|
|
342
|
+
second_speed = active_points_list[second].get("speed") or 0.0
|
|
343
|
+
|
|
344
|
+
# Calculate aspect movement (applying/separating/fixed)
|
|
345
|
+
aspect_movement = calculate_aspect_movement(
|
|
346
|
+
active_points_list[first]["abs_pos"],
|
|
347
|
+
active_points_list[second]["abs_pos"],
|
|
348
|
+
aspect["aspect_degrees"],
|
|
349
|
+
first_speed,
|
|
350
|
+
second_speed
|
|
351
|
+
)
|
|
339
352
|
|
|
340
353
|
aspect_model = AspectModel(
|
|
341
354
|
p1_name=first_name,
|
|
@@ -412,12 +425,24 @@ class AspectsFactory:
|
|
|
412
425
|
first_planet_id = planet_id_lookup.get(first_name, 0)
|
|
413
426
|
second_planet_id = planet_id_lookup.get(second_name, 0)
|
|
414
427
|
|
|
415
|
-
#
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
428
|
+
# For aspects between axes (ASC, MC, DSC, IC) in different charts
|
|
429
|
+
# there is no meaningful dynamic movement between two house systems,
|
|
430
|
+
# so we mark the movement as "Fixed".
|
|
431
|
+
if first_name in AXES_LIST and second_name in AXES_LIST:
|
|
432
|
+
aspect_movement = "Fixed"
|
|
433
|
+
else:
|
|
434
|
+
# Get speeds, fall back to 0.0 only if missing/None
|
|
435
|
+
first_speed = first_active_points_list[first].get("speed") or 0.0
|
|
436
|
+
second_speed = second_active_points_list[second].get("speed") or 0.0
|
|
437
|
+
|
|
438
|
+
# Calculate aspect movement (applying/separating/fixed)
|
|
439
|
+
aspect_movement = calculate_aspect_movement(
|
|
440
|
+
first_active_points_list[first]["abs_pos"],
|
|
441
|
+
second_active_points_list[second]["abs_pos"],
|
|
442
|
+
aspect["aspect_degrees"],
|
|
443
|
+
first_speed,
|
|
444
|
+
second_speed
|
|
445
|
+
)
|
|
421
446
|
|
|
422
447
|
aspect_model = AspectModel(
|
|
423
448
|
p1_name=first_name,
|
|
@@ -565,4 +590,3 @@ if __name__ == "__main__":
|
|
|
565
590
|
yoko = AstrologicalSubjectFactory.from_birth_data("Yoko", 1933, 2, 18, 10, 30, "Tokyo", "JP")
|
|
566
591
|
dual_chart_aspects = AspectsFactory.dual_chart_aspects(john, yoko)
|
|
567
592
|
print(f"Dual chart aspects: {len(dual_chart_aspects.aspects)}")
|
|
568
|
-
|
|
@@ -56,75 +56,182 @@ def get_aspect_from_two_points(
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
def _sign(x: float) -> int:
|
|
60
|
+
"""
|
|
61
|
+
Return the sign of ``x``.
|
|
62
|
+
|
|
63
|
+
Returns +1 if ``x`` > 0, -1 if ``x`` < 0, and 0 if ``x`` == 0.
|
|
64
|
+
"""
|
|
65
|
+
if x > 0:
|
|
66
|
+
return 1
|
|
67
|
+
elif x < 0:
|
|
68
|
+
return -1
|
|
69
|
+
else:
|
|
70
|
+
return 0
|
|
71
|
+
|
|
72
|
+
|
|
59
73
|
def calculate_aspect_movement(
|
|
60
74
|
point_one_abs_pos: float,
|
|
61
75
|
point_two_abs_pos: float,
|
|
62
|
-
aspect_degrees:
|
|
63
|
-
|
|
76
|
+
aspect_degrees: float,
|
|
77
|
+
point_one_speed: float,
|
|
78
|
+
point_two_speed: float,
|
|
64
79
|
) -> AspectMovementType:
|
|
65
80
|
"""
|
|
66
|
-
|
|
81
|
+
Determine whether an aspect is applying, separating, or fixed.
|
|
67
82
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- "Applying": When the faster planet is moving toward the exact aspect
|
|
71
|
-
- "Separating": When the faster planet is moving away from the exact aspect
|
|
83
|
+
This implementation uses a dynamic definition based on the time evolution
|
|
84
|
+
of the orb:
|
|
72
85
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
- "Applying": the orb is decreasing with time (the aspect is moving toward
|
|
87
|
+
exactness).
|
|
88
|
+
- "Separating": the orb is increasing with time (the aspect has already
|
|
89
|
+
perfected in the past, or will not perfect given the current motion).
|
|
90
|
+
- "Fixed": both points are effectively fixed so the orb does not change.
|
|
78
91
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
point_two_abs_pos: Absolute position of second point (0-360°)
|
|
82
|
-
aspect_degrees: The exact degree of the aspect (0, 60, 90, 120, 180, etc.)
|
|
83
|
-
exact_orb_threshold: Maximum orb to consider aspect "exact" (default 0.17°)
|
|
92
|
+
Motion direction (direct or retrograde) is taken from the sign of the
|
|
93
|
+
speed values:
|
|
84
94
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
- speed > 0: direct motion (increasing longitude)
|
|
96
|
+
- speed < 0: retrograde motion (decreasing longitude)
|
|
97
|
+
|
|
98
|
+
The algorithm does not assume which point is "faster" in absolute terms;
|
|
99
|
+
it uses the relative motion to determine whether the orb is growing or
|
|
100
|
+
shrinking.
|
|
101
|
+
|
|
102
|
+
Definitions:
|
|
103
|
+
|
|
104
|
+
- Longitudes are in degrees, 0 <= λ < 360 (normalized internally).
|
|
105
|
+
- ``aspect_degrees`` is the nominal angle of the aspect; values > 180
|
|
106
|
+
are made symmetric (e.g. 240° becomes 120°).
|
|
107
|
+
- The orb is defined as::
|
|
108
|
+
|
|
109
|
+
orb = abs(abs(separation) - aspect)
|
|
110
|
+
|
|
111
|
+
where ``separation`` is the minimal angular distance between the two
|
|
112
|
+
points in [-180, 180).
|
|
113
|
+
|
|
114
|
+
Logical steps:
|
|
115
|
+
|
|
116
|
+
1. Normalize longitudes to [0, 360) and the aspect to [0, 180].
|
|
117
|
+
2. Compute the signed separation ``sep`` between the two points using
|
|
118
|
+
``difdeg2n``.
|
|
119
|
+
3. Compute the current orb::
|
|
120
|
+
|
|
121
|
+
sep_abs = abs(sep)
|
|
122
|
+
orb = abs(sep_abs - aspect)
|
|
123
|
+
|
|
124
|
+
4. Compute the relevant speed:
|
|
125
|
+
- if both points move: ``moving_speed = point_two_speed - point_one_speed``
|
|
126
|
+
- if one point is fixed (speed == 0): use the moving point speed
|
|
127
|
+
against the fixed point.
|
|
128
|
+
5. The qualitative sign of the orb derivative is::
|
|
129
|
+
|
|
130
|
+
sign_d_orb = sign(sep_abs - aspect) * sign(sep) * sign(moving_speed)
|
|
131
|
+
|
|
132
|
+
If ``sign_d_orb < 0`` the orb decreases (applying), if
|
|
133
|
+
``sign_d_orb > 0`` the orb increases (separating).
|
|
134
|
+
6. If the relevant speed is exactly zero, the orb does not change over
|
|
135
|
+
time; if it is not exact, it is considered separating by convention.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
point_one_abs_pos: Absolute longitude of the first point in degrees.
|
|
139
|
+
point_two_abs_pos: Absolute longitude of the second point in degrees.
|
|
140
|
+
aspect_degrees: Nominal aspect angle (e.g. 0, 60, 90, 120, 180).
|
|
141
|
+
point_one_speed: Speed of the first point in degrees/day (signed).
|
|
142
|
+
point_two_speed: Speed of the second point in degrees/day (signed).
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
AspectMovementType: ``"Applying"``, ``"Separating"`` or ``"Fixed"``.
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
ValueError: If any of the speeds is ``None``.
|
|
149
|
+
|
|
150
|
+
Examples:
|
|
151
|
+
>>> # Fast Moon at 45° approaching Sun at 50° (conjunction)
|
|
152
|
+
>>> calculate_aspect_movement(45, 50, 0, 12.5, 1.0)
|
|
153
|
+
'Applying'
|
|
154
|
+
|
|
155
|
+
>>> # Moon at 55° moving away from Sun at 50° (conjunction)
|
|
156
|
+
>>> calculate_aspect_movement(55, 50, 0, 12.5, 1.0)
|
|
157
|
+
'Separating'
|
|
158
|
+
|
|
159
|
+
>>> # Mercury (fast) square Mars
|
|
160
|
+
>>> calculate_aspect_movement(5, 100, 90, 1.5, 0.5)
|
|
161
|
+
'Applying'
|
|
162
|
+
|
|
163
|
+
>>> # Venus separating from a trine to Jupiter
|
|
164
|
+
>>> calculate_aspect_movement(5, 127, 120, 0.1, 1.2)
|
|
165
|
+
'Separating'
|
|
166
|
+
|
|
167
|
+
>>> # Retrograde Mars applying to a conjunction with direct Jupiter
|
|
168
|
+
>>> calculate_aspect_movement(110, 100, 0, -0.8, 0.1)
|
|
169
|
+
'Applying'
|
|
95
170
|
"""
|
|
96
171
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
172
|
+
if point_one_speed is None or point_two_speed is None:
|
|
173
|
+
raise ValueError(
|
|
174
|
+
"Speed values for both points are required to compute aspect "
|
|
175
|
+
"movement correctly. point_one_speed and point_two_speed "
|
|
176
|
+
"cannot be None."
|
|
177
|
+
)
|
|
100
178
|
|
|
101
|
-
#
|
|
102
|
-
|
|
103
|
-
|
|
179
|
+
# Normalize longitudes to [0, 360)
|
|
180
|
+
p1 = point_one_abs_pos % 360.0
|
|
181
|
+
p2 = point_two_abs_pos % 360.0
|
|
104
182
|
|
|
105
|
-
#
|
|
106
|
-
|
|
107
|
-
|
|
183
|
+
# Normalize aspect to [0, 360) and then to [0, 180]
|
|
184
|
+
aspect = aspect_degrees % 360.0
|
|
185
|
+
if aspect > 180.0:
|
|
186
|
+
aspect = 360.0 - aspect # es. 240 -> 120
|
|
108
187
|
|
|
109
|
-
#
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
188
|
+
# Special case: if one of the two points is effectively fixed (speed = 0),
|
|
189
|
+
# such as chart angles, we consider only the motion of the moving point
|
|
190
|
+
# relative to the fixed one. The order of the points must not affect
|
|
191
|
+
# the result.
|
|
192
|
+
if point_one_speed == 0 and point_two_speed == 0:
|
|
193
|
+
# Both points are fixed, the aspect never changes dynamically
|
|
194
|
+
return "Fixed"
|
|
114
195
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
196
|
+
# Identify which point is moving and which one is fixed/slower.
|
|
197
|
+
# To correctly handle axes we always compute with respect to the
|
|
198
|
+
# moving point.
|
|
199
|
+
if point_one_speed == 0:
|
|
200
|
+
# point_one is fixed (e.g. axis), point_two moves
|
|
201
|
+
moving_pos = p2
|
|
202
|
+
fixed_pos = p1
|
|
203
|
+
moving_speed = point_two_speed
|
|
204
|
+
sep = difdeg2n(moving_pos, fixed_pos)
|
|
205
|
+
|
|
206
|
+
elif point_two_speed == 0:
|
|
207
|
+
# point_two is fixed (e.g. axis), point_one moves
|
|
208
|
+
moving_pos = p1
|
|
209
|
+
fixed_pos = p2
|
|
210
|
+
moving_speed = point_one_speed
|
|
211
|
+
sep = difdeg2n(moving_pos, fixed_pos)
|
|
118
212
|
|
|
119
213
|
else:
|
|
120
|
-
#
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
214
|
+
# Both points move: use relative speed and standard separation
|
|
215
|
+
sep = difdeg2n(p2, p1)
|
|
216
|
+
moving_speed = point_two_speed - point_one_speed
|
|
217
|
+
|
|
218
|
+
sep_abs = abs(sep)
|
|
219
|
+
|
|
220
|
+
# If the (relative or absolute) speed is zero, the orb does not change
|
|
221
|
+
if moving_speed == 0:
|
|
222
|
+
return "Separating"
|
|
223
|
+
|
|
224
|
+
# Signs used to determine whether the orb grows or shrinks
|
|
225
|
+
sign_sep = _sign(sep) # sign of the separation
|
|
226
|
+
sign_delta = _sign(sep_abs - aspect) # whether we are "before" or "beyond" the exact aspect
|
|
227
|
+
sign_rel = _sign(moving_speed) # direction in which the separation evolves
|
|
228
|
+
|
|
229
|
+
# Qualitative sign of the orb derivative (d(orb)/dt):
|
|
230
|
+
# < 0 -> orb decreases -> Applying
|
|
231
|
+
# > 0 -> orb increases -> Separating
|
|
232
|
+
orb_derivative_sign = sign_delta * sign_sep * sign_rel
|
|
233
|
+
|
|
234
|
+
return "Applying" if orb_derivative_sign < 0 else "Separating"
|
|
128
235
|
|
|
129
236
|
|
|
130
237
|
def planet_id_decoder(planets_settings: list[KerykeionSettingsCelestialPointModel], name: str) -> int:
|
|
@@ -1088,16 +1088,24 @@ class AstrologicalSubjectFactory:
|
|
|
1088
1088
|
# Calculate axis points
|
|
1089
1089
|
point_type = "AstrologicalPoint"
|
|
1090
1090
|
|
|
1091
|
+
# NOTE: Swiss Ephemeris does not provide direct speeds for angles (ASC/MC),
|
|
1092
|
+
# but in realtà si muovono molto velocemente rispetto ai pianeti.
|
|
1093
|
+
# Per rappresentare questo in modo coerente, assegniamo ai quattro assi
|
|
1094
|
+
# una speed sintetica fissa, molto più alta di quella planetaria tipica.
|
|
1095
|
+
# Questo permette a calculate_aspect_movement di considerarli sempre come
|
|
1096
|
+
# "più veloci" rispetto ai pianeti quando serve.
|
|
1097
|
+
axis_speed = 360.0 # gradi/giorno, valore simbolico ma coerente
|
|
1098
|
+
|
|
1091
1099
|
# Calculate Ascendant if needed
|
|
1092
1100
|
if should_calculate("Ascendant"):
|
|
1093
|
-
data["ascendant"] = get_kerykeion_point_from_degree(ascmc[0], "Ascendant", point_type=point_type)
|
|
1101
|
+
data["ascendant"] = get_kerykeion_point_from_degree(ascmc[0], "Ascendant", point_type=point_type, speed=axis_speed)
|
|
1094
1102
|
data["ascendant"].house = get_planet_house(data["ascendant"].abs_pos, data["_houses_degree_ut"])
|
|
1095
1103
|
data["ascendant"].retrograde = False
|
|
1096
1104
|
calculated_axial_cusps.append("Ascendant")
|
|
1097
1105
|
|
|
1098
1106
|
# Calculate Medium Coeli if needed
|
|
1099
1107
|
if should_calculate("Medium_Coeli"):
|
|
1100
|
-
data["medium_coeli"] = get_kerykeion_point_from_degree(ascmc[1], "Medium_Coeli", point_type=point_type)
|
|
1108
|
+
data["medium_coeli"] = get_kerykeion_point_from_degree(ascmc[1], "Medium_Coeli", point_type=point_type, speed=axis_speed)
|
|
1101
1109
|
data["medium_coeli"].house = get_planet_house(data["medium_coeli"].abs_pos, data["_houses_degree_ut"])
|
|
1102
1110
|
data["medium_coeli"].retrograde = False
|
|
1103
1111
|
calculated_axial_cusps.append("Medium_Coeli")
|
|
@@ -1105,7 +1113,7 @@ class AstrologicalSubjectFactory:
|
|
|
1105
1113
|
# Calculate Descendant if needed
|
|
1106
1114
|
if should_calculate("Descendant"):
|
|
1107
1115
|
dsc_deg = math.fmod(ascmc[0] + 180, 360)
|
|
1108
|
-
data["descendant"] = get_kerykeion_point_from_degree(dsc_deg, "Descendant", point_type=point_type)
|
|
1116
|
+
data["descendant"] = get_kerykeion_point_from_degree(dsc_deg, "Descendant", point_type=point_type, speed=axis_speed)
|
|
1109
1117
|
data["descendant"].house = get_planet_house(data["descendant"].abs_pos, data["_houses_degree_ut"])
|
|
1110
1118
|
data["descendant"].retrograde = False
|
|
1111
1119
|
calculated_axial_cusps.append("Descendant")
|
|
@@ -1113,7 +1121,7 @@ class AstrologicalSubjectFactory:
|
|
|
1113
1121
|
# Calculate Imum Coeli if needed
|
|
1114
1122
|
if should_calculate("Imum_Coeli"):
|
|
1115
1123
|
ic_deg = math.fmod(ascmc[1] + 180, 360)
|
|
1116
|
-
data["imum_coeli"] = get_kerykeion_point_from_degree(ic_deg, "Imum_Coeli", point_type=point_type)
|
|
1124
|
+
data["imum_coeli"] = get_kerykeion_point_from_degree(ic_deg, "Imum_Coeli", point_type=point_type, speed=axis_speed)
|
|
1117
1125
|
data["imum_coeli"].house = get_planet_house(data["imum_coeli"].abs_pos, data["_houses_degree_ut"])
|
|
1118
1126
|
data["imum_coeli"].retrograde = False
|
|
1119
1127
|
calculated_axial_cusps.append("Imum_Coeli")
|
|
@@ -1463,89 +1471,92 @@ class AstrologicalSubjectFactory:
|
|
|
1463
1471
|
# TRANS-NEPTUNIAN OBJECTS
|
|
1464
1472
|
# ==================
|
|
1465
1473
|
|
|
1474
|
+
# For TNOs we compute ecliptic longitude for zodiac placement and
|
|
1475
|
+
# declination from equatorial coordinates, same as other bodies.
|
|
1476
|
+
|
|
1466
1477
|
# Calculate Eris
|
|
1467
1478
|
if should_calculate("Eris"):
|
|
1468
1479
|
try:
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
calculated_planets.append("Eris")
|
|
1480
|
+
AstrologicalSubjectFactory._calculate_single_planet(
|
|
1481
|
+
data, "Eris", swe.AST_OFFSET + 136199, julian_day, iflag,
|
|
1482
|
+
houses_degree_ut, point_type, calculated_planets, active_points
|
|
1483
|
+
)
|
|
1474
1484
|
except Exception as e:
|
|
1475
1485
|
logging.warning(f"Could not calculate Eris position: {e}")
|
|
1476
|
-
|
|
1486
|
+
if "Eris" in active_points:
|
|
1487
|
+
active_points.remove("Eris") # Remove if not calculated
|
|
1477
1488
|
|
|
1478
1489
|
# Calculate Sedna
|
|
1479
1490
|
if should_calculate("Sedna"):
|
|
1480
1491
|
try:
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
calculated_planets.append("Sedna")
|
|
1492
|
+
AstrologicalSubjectFactory._calculate_single_planet(
|
|
1493
|
+
data, "Sedna", swe.AST_OFFSET + 90377, julian_day, iflag,
|
|
1494
|
+
houses_degree_ut, point_type, calculated_planets, active_points
|
|
1495
|
+
)
|
|
1486
1496
|
except Exception as e:
|
|
1487
1497
|
logging.warning(f"Could not calculate Sedna position: {e}")
|
|
1488
|
-
|
|
1498
|
+
if "Sedna" in active_points:
|
|
1499
|
+
active_points.remove("Sedna")
|
|
1489
1500
|
|
|
1490
1501
|
# Calculate Haumea
|
|
1491
1502
|
if should_calculate("Haumea"):
|
|
1492
1503
|
try:
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
calculated_planets.append("Haumea")
|
|
1504
|
+
AstrologicalSubjectFactory._calculate_single_planet(
|
|
1505
|
+
data, "Haumea", swe.AST_OFFSET + 136108, julian_day, iflag,
|
|
1506
|
+
houses_degree_ut, point_type, calculated_planets, active_points
|
|
1507
|
+
)
|
|
1498
1508
|
except Exception as e:
|
|
1499
1509
|
logging.warning(f"Could not calculate Haumea position: {e}")
|
|
1500
|
-
|
|
1510
|
+
if "Haumea" in active_points:
|
|
1511
|
+
active_points.remove("Haumea") # Remove if not calculated
|
|
1501
1512
|
|
|
1502
1513
|
# Calculate Makemake
|
|
1503
1514
|
if should_calculate("Makemake"):
|
|
1504
1515
|
try:
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
calculated_planets.append("Makemake")
|
|
1516
|
+
AstrologicalSubjectFactory._calculate_single_planet(
|
|
1517
|
+
data, "Makemake", swe.AST_OFFSET + 136472, julian_day, iflag,
|
|
1518
|
+
houses_degree_ut, point_type, calculated_planets, active_points
|
|
1519
|
+
)
|
|
1510
1520
|
except Exception as e:
|
|
1511
1521
|
logging.warning(f"Could not calculate Makemake position: {e}")
|
|
1512
|
-
|
|
1522
|
+
if "Makemake" in active_points:
|
|
1523
|
+
active_points.remove("Makemake") # Remove if not calculated
|
|
1513
1524
|
|
|
1514
1525
|
# Calculate Ixion
|
|
1515
1526
|
if should_calculate("Ixion"):
|
|
1516
1527
|
try:
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
calculated_planets.append("Ixion")
|
|
1528
|
+
AstrologicalSubjectFactory._calculate_single_planet(
|
|
1529
|
+
data, "Ixion", swe.AST_OFFSET + 28978, julian_day, iflag,
|
|
1530
|
+
houses_degree_ut, point_type, calculated_planets, active_points
|
|
1531
|
+
)
|
|
1522
1532
|
except Exception as e:
|
|
1523
1533
|
logging.warning(f"Could not calculate Ixion position: {e}")
|
|
1524
|
-
|
|
1534
|
+
if "Ixion" in active_points:
|
|
1535
|
+
active_points.remove("Ixion") # Remove if not calculated
|
|
1525
1536
|
|
|
1526
1537
|
# Calculate Orcus
|
|
1527
1538
|
if should_calculate("Orcus"):
|
|
1528
1539
|
try:
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
calculated_planets.append("Orcus")
|
|
1540
|
+
AstrologicalSubjectFactory._calculate_single_planet(
|
|
1541
|
+
data, "Orcus", swe.AST_OFFSET + 90482, julian_day, iflag,
|
|
1542
|
+
houses_degree_ut, point_type, calculated_planets, active_points
|
|
1543
|
+
)
|
|
1534
1544
|
except Exception as e:
|
|
1535
1545
|
logging.warning(f"Could not calculate Orcus position: {e}")
|
|
1536
|
-
|
|
1546
|
+
if "Orcus" in active_points:
|
|
1547
|
+
active_points.remove("Orcus") # Remove if not calculated
|
|
1537
1548
|
|
|
1538
1549
|
# Calculate Quaoar
|
|
1539
1550
|
if should_calculate("Quaoar"):
|
|
1540
1551
|
try:
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
calculated_planets.append("Quaoar")
|
|
1552
|
+
AstrologicalSubjectFactory._calculate_single_planet(
|
|
1553
|
+
data, "Quaoar", swe.AST_OFFSET + 50000, julian_day, iflag,
|
|
1554
|
+
houses_degree_ut, point_type, calculated_planets, active_points
|
|
1555
|
+
)
|
|
1546
1556
|
except Exception as e:
|
|
1547
1557
|
logging.warning(f"Could not calculate Quaoar position: {e}")
|
|
1548
|
-
|
|
1558
|
+
if "Quaoar" in active_points:
|
|
1559
|
+
active_points.remove("Quaoar") # Remove if not calculated
|
|
1549
1560
|
|
|
1550
1561
|
# ==================
|
|
1551
1562
|
# FIXED STARS
|
|
@@ -1555,33 +1566,41 @@ class AstrologicalSubjectFactory:
|
|
|
1555
1566
|
if should_calculate("Regulus"):
|
|
1556
1567
|
try:
|
|
1557
1568
|
star_name = "Regulus"
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1569
|
+
# Ecliptic longitude for zodiac placement
|
|
1570
|
+
pos_ecl = swe.fixstar_ut(star_name, julian_day, iflag)[0]
|
|
1571
|
+
regulus_deg = pos_ecl[0]
|
|
1572
|
+
regulus_speed = pos_ecl[3] if len(pos_ecl) > 3 else 0.0 # Fixed stars have very slow speed
|
|
1573
|
+
# Equatorial coordinates for true declination
|
|
1574
|
+
pos_eq = swe.fixstar_ut(star_name, julian_day, iflag | swe.FLG_EQUATORIAL)[0]
|
|
1575
|
+
regulus_dec = pos_eq[1] if len(pos_eq) > 1 else None
|
|
1562
1576
|
data["regulus"] = get_kerykeion_point_from_degree(regulus_deg, "Regulus", point_type=point_type, speed=regulus_speed, declination=regulus_dec)
|
|
1563
1577
|
data["regulus"].house = get_planet_house(regulus_deg, houses_degree_ut)
|
|
1564
1578
|
data["regulus"].retrograde = False # Fixed stars are never retrograde
|
|
1565
1579
|
calculated_planets.append("Regulus")
|
|
1566
1580
|
except Exception as e:
|
|
1567
1581
|
logging.warning(f"Could not calculate Regulus position: {e}")
|
|
1568
|
-
|
|
1582
|
+
if "Regulus" in active_points:
|
|
1583
|
+
active_points.remove("Regulus") # Remove if not calculated
|
|
1569
1584
|
|
|
1570
1585
|
# Calculate Spica (example fixed star)
|
|
1571
1586
|
if should_calculate("Spica"):
|
|
1572
1587
|
try:
|
|
1573
1588
|
star_name = "Spica"
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1589
|
+
# Ecliptic longitude for zodiac placement
|
|
1590
|
+
pos_ecl = swe.fixstar_ut(star_name, julian_day, iflag)[0]
|
|
1591
|
+
spica_deg = pos_ecl[0]
|
|
1592
|
+
spica_speed = pos_ecl[3] if len(pos_ecl) > 3 else 0.0 # Fixed stars have very slow speed
|
|
1593
|
+
# Equatorial coordinates for true declination
|
|
1594
|
+
pos_eq = swe.fixstar_ut(star_name, julian_day, iflag | swe.FLG_EQUATORIAL)[0]
|
|
1595
|
+
spica_dec = pos_eq[1] if len(pos_eq) > 1 else None
|
|
1578
1596
|
data["spica"] = get_kerykeion_point_from_degree(spica_deg, "Spica", point_type=point_type, speed=spica_speed, declination=spica_dec)
|
|
1579
1597
|
data["spica"].house = get_planet_house(spica_deg, houses_degree_ut)
|
|
1580
1598
|
data["spica"].retrograde = False # Fixed stars are never retrograde
|
|
1581
1599
|
calculated_planets.append("Spica")
|
|
1582
1600
|
except Exception as e:
|
|
1583
1601
|
logging.warning(f"Could not calculate Spica position: {e}")
|
|
1584
|
-
|
|
1602
|
+
if "Spica" in active_points:
|
|
1603
|
+
active_points.remove("Spica") # Remove if not calculated
|
|
1585
1604
|
|
|
1586
1605
|
# ==================
|
|
1587
1606
|
# ARABIC PARTS / LOTS
|