kerykeion 5.0.0a9__py3-none-any.whl → 5.1.8__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 +50 -9
- kerykeion/aspects/__init__.py +5 -2
- kerykeion/aspects/aspects_factory.py +568 -0
- kerykeion/aspects/aspects_utils.py +78 -11
- kerykeion/astrological_subject_factory.py +1032 -275
- kerykeion/backword.py +820 -0
- kerykeion/chart_data_factory.py +552 -0
- kerykeion/charts/chart_drawer.py +2661 -0
- kerykeion/charts/charts_utils.py +652 -399
- kerykeion/charts/draw_planets.py +603 -353
- kerykeion/charts/templates/aspect_grid_only.xml +326 -198
- kerykeion/charts/templates/chart.xml +306 -256
- kerykeion/charts/templates/wheel_only.xml +330 -200
- kerykeion/charts/themes/black-and-white.css +148 -0
- kerykeion/charts/themes/classic.css +11 -0
- kerykeion/charts/themes/dark-high-contrast.css +11 -0
- kerykeion/charts/themes/dark.css +11 -0
- kerykeion/charts/themes/light.css +11 -0
- kerykeion/charts/themes/strawberry.css +10 -0
- kerykeion/composite_subject_factory.py +232 -13
- kerykeion/ephemeris_data_factory.py +443 -0
- kerykeion/fetch_geonames.py +78 -21
- kerykeion/house_comparison/__init__.py +4 -1
- kerykeion/house_comparison/house_comparison_factory.py +52 -19
- kerykeion/house_comparison/house_comparison_utils.py +37 -9
- kerykeion/kr_types/__init__.py +66 -6
- kerykeion/kr_types/chart_template_model.py +20 -0
- kerykeion/kr_types/kerykeion_exception.py +15 -9
- kerykeion/kr_types/kr_literals.py +14 -160
- kerykeion/kr_types/kr_models.py +14 -291
- kerykeion/kr_types/settings_models.py +15 -167
- kerykeion/planetary_return_factory.py +545 -40
- kerykeion/relationship_score_factory.py +137 -63
- kerykeion/report.py +749 -64
- kerykeion/schemas/__init__.py +106 -0
- kerykeion/schemas/chart_template_model.py +367 -0
- kerykeion/schemas/kerykeion_exception.py +20 -0
- kerykeion/schemas/kr_literals.py +181 -0
- kerykeion/schemas/kr_models.py +603 -0
- kerykeion/schemas/settings_models.py +188 -0
- kerykeion/settings/__init__.py +20 -1
- kerykeion/settings/chart_defaults.py +444 -0
- kerykeion/settings/config_constants.py +88 -12
- kerykeion/settings/kerykeion_settings.py +32 -75
- kerykeion/settings/translation_strings.py +1499 -0
- kerykeion/settings/translations.py +74 -0
- kerykeion/sweph/ast136/s136108s.se1 +0 -0
- kerykeion/sweph/ast136/s136199s.se1 +0 -0
- kerykeion/sweph/ast136/s136472s.se1 +0 -0
- kerykeion/sweph/ast28/se28978s.se1 +0 -0
- kerykeion/sweph/ast50/se50000s.se1 +0 -0
- kerykeion/sweph/ast90/se90377s.se1 +0 -0
- kerykeion/sweph/ast90/se90482s.se1 +0 -0
- kerykeion/sweph/sefstars.txt +1602 -0
- kerykeion/transits_time_range_factory.py +302 -0
- kerykeion/utilities.py +289 -204
- kerykeion-5.1.8.dist-info/METADATA +1793 -0
- kerykeion-5.1.8.dist-info/RECORD +63 -0
- kerykeion/aspects/natal_aspects.py +0 -181
- kerykeion/aspects/synastry_aspects.py +0 -141
- kerykeion/aspects/transits_time_range.py +0 -41
- kerykeion/charts/draw_planets_v2.py +0 -649
- kerykeion/charts/draw_planets_v3.py +0 -679
- kerykeion/charts/kerykeion_chart_svg.py +0 -2038
- kerykeion/enums.py +0 -57
- kerykeion/ephemeris_data.py +0 -238
- kerykeion/house_comparison/house_comparison_models.py +0 -38
- kerykeion/kr_types/chart_types.py +0 -106
- kerykeion/settings/kr.config.json +0 -1304
- kerykeion/settings/legacy/__init__.py +0 -0
- kerykeion/settings/legacy/legacy_celestial_points_settings.py +0 -299
- kerykeion/settings/legacy/legacy_chart_aspects_settings.py +0 -71
- kerykeion/settings/legacy/legacy_color_settings.py +0 -42
- kerykeion/transits_time_range.py +0 -128
- kerykeion-5.0.0a9.dist-info/METADATA +0 -636
- kerykeion-5.0.0a9.dist-info/RECORD +0 -55
- kerykeion-5.0.0a9.dist-info/entry_points.txt +0 -2
- {kerykeion-5.0.0a9.dist-info → kerykeion-5.1.8.dist-info}/WHEEL +0 -0
- {kerykeion-5.0.0a9.dist-info → kerykeion-5.1.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,2038 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
3
|
-
This is part of Kerykeion (C) 2025 Giacomo Battaglia
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import logging
|
|
8
|
-
import swisseph as swe
|
|
9
|
-
from typing import get_args, Union, Optional
|
|
10
|
-
|
|
11
|
-
from kerykeion.settings.kerykeion_settings import get_settings
|
|
12
|
-
from kerykeion.aspects.synastry_aspects import SynastryAspects
|
|
13
|
-
from kerykeion.aspects.natal_aspects import NatalAspects
|
|
14
|
-
from kerykeion.house_comparison.house_comparison_factory import HouseComparisonFactory
|
|
15
|
-
from kerykeion.kr_types import (
|
|
16
|
-
KerykeionException,
|
|
17
|
-
ChartType,
|
|
18
|
-
Sign,
|
|
19
|
-
ActiveAspect,
|
|
20
|
-
)
|
|
21
|
-
from kerykeion.kr_types import ChartTemplateDictionary
|
|
22
|
-
from kerykeion.kr_types.kr_models import (
|
|
23
|
-
AstrologicalSubjectModel,
|
|
24
|
-
CompositeSubjectModel,
|
|
25
|
-
PlanetReturnModel,
|
|
26
|
-
)
|
|
27
|
-
from kerykeion.kr_types.settings_models import (
|
|
28
|
-
KerykeionSettingsCelestialPointModel,
|
|
29
|
-
KerykeionSettingsModel,
|
|
30
|
-
)
|
|
31
|
-
from kerykeion.kr_types.kr_literals import (
|
|
32
|
-
KerykeionChartTheme,
|
|
33
|
-
KerykeionChartLanguage,
|
|
34
|
-
AstrologicalPoint,
|
|
35
|
-
)
|
|
36
|
-
from kerykeion.utilities import find_common_active_points
|
|
37
|
-
from kerykeion.charts.charts_utils import (
|
|
38
|
-
draw_zodiac_slice,
|
|
39
|
-
convert_latitude_coordinate_to_string,
|
|
40
|
-
convert_longitude_coordinate_to_string,
|
|
41
|
-
draw_aspect_line,
|
|
42
|
-
draw_transit_ring_degree_steps,
|
|
43
|
-
draw_degree_ring,
|
|
44
|
-
draw_transit_ring,
|
|
45
|
-
draw_background_circle,
|
|
46
|
-
draw_first_circle,
|
|
47
|
-
draw_house_comparison_grid,
|
|
48
|
-
draw_second_circle,
|
|
49
|
-
draw_third_circle,
|
|
50
|
-
draw_aspect_grid,
|
|
51
|
-
draw_houses_cusps_and_text_number,
|
|
52
|
-
draw_transit_aspect_list,
|
|
53
|
-
draw_transit_aspect_grid,
|
|
54
|
-
draw_single_house_comparison_grid,
|
|
55
|
-
makeLunarPhase,
|
|
56
|
-
draw_main_house_grid,
|
|
57
|
-
draw_secondary_house_grid,
|
|
58
|
-
draw_main_planet_grid,
|
|
59
|
-
draw_secondary_planet_grid,
|
|
60
|
-
format_location_string,
|
|
61
|
-
format_datetime_with_timezone,
|
|
62
|
-
calculate_element_points,
|
|
63
|
-
calculate_synastry_element_points,
|
|
64
|
-
calculate_quality_points,
|
|
65
|
-
calculate_synastry_quality_points
|
|
66
|
-
)
|
|
67
|
-
from kerykeion.charts.draw_planets_v2 import draw_planets_v2 as draw_planets
|
|
68
|
-
from kerykeion.utilities import get_houses_list, inline_css_variables_in_svg
|
|
69
|
-
from kerykeion.settings.config_constants import DEFAULT_ACTIVE_ASPECTS
|
|
70
|
-
from kerykeion.settings.legacy.legacy_color_settings import DEFAULT_CHART_COLORS
|
|
71
|
-
from kerykeion.settings.legacy.legacy_celestial_points_settings import DEFAULT_CELESTIAL_POINTS_SETTINGS
|
|
72
|
-
from kerykeion.settings.legacy.legacy_chart_aspects_settings import DEFAULT_CHART_ASPECTS_SETTINGS
|
|
73
|
-
from pathlib import Path
|
|
74
|
-
from scour.scour import scourString
|
|
75
|
-
from string import Template
|
|
76
|
-
from typing import Union, List, Literal
|
|
77
|
-
from datetime import datetime
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class KerykeionChartSVG:
|
|
81
|
-
"""
|
|
82
|
-
KerykeionChartSVG generates astrological chart visualizations as SVG files.
|
|
83
|
-
|
|
84
|
-
This class supports creating full chart SVGs, wheel-only SVGs, and aspect-grid-only SVGs
|
|
85
|
-
for various chart types including Natal, ExternalNatal, Transit, Synastry, and Composite.
|
|
86
|
-
Charts are rendered using XML templates and drawing utilities, with customizable themes,
|
|
87
|
-
language, active points, and aspects.
|
|
88
|
-
The rendered SVGs can be saved to a specified output directory or, by default, to the user's home directory.
|
|
89
|
-
|
|
90
|
-
NOTE:
|
|
91
|
-
The generated SVG files are optimized for web use, opening in browsers. If you want to
|
|
92
|
-
use them in other applications, you might need to adjust the SVG settings or styles.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
first_obj (AstrologicalSubject | AstrologicalSubjectModel | CompositeSubjectModel):
|
|
96
|
-
The primary astrological subject for the chart.
|
|
97
|
-
chart_type (ChartType, optional):
|
|
98
|
-
The type of chart to generate ('Natal', 'ExternalNatal', 'Transit', 'Synastry', 'Composite').
|
|
99
|
-
Defaults to 'Natal'.
|
|
100
|
-
second_obj (AstrologicalSubject | AstrologicalSubjectModel, optional):
|
|
101
|
-
The secondary subject for Transit or Synastry charts. Not required for Natal or Composite.
|
|
102
|
-
new_output_directory (str | Path, optional):
|
|
103
|
-
Directory to write generated SVG files. Defaults to the user's home directory.
|
|
104
|
-
new_settings_file (Path | dict | KerykeionSettingsModel, optional):
|
|
105
|
-
Path or settings object to override default chart configuration (colors, fonts, aspects).
|
|
106
|
-
theme (KerykeionChartTheme, optional):
|
|
107
|
-
CSS theme for the chart. If None, no default styles are applied. Defaults to 'classic'.
|
|
108
|
-
double_chart_aspect_grid_type (Literal['list', 'table'], optional):
|
|
109
|
-
Specifies rendering style for double-chart aspect grids. Defaults to 'list'.
|
|
110
|
-
chart_language (KerykeionChartLanguage, optional):
|
|
111
|
-
Language code for chart labels. Defaults to 'EN'.
|
|
112
|
-
active_points (list[AstrologicalPoint], optional):
|
|
113
|
-
List of celestial points and angles to include. Defaults to DEFAULT_ACTIVE_POINTS.
|
|
114
|
-
Example:
|
|
115
|
-
["Sun", "Moon", "Mercury", "Venus"]
|
|
116
|
-
|
|
117
|
-
active_aspects (list[ActiveAspect], optional):
|
|
118
|
-
List of aspects (name and orb) to calculate. Defaults to DEFAULT_ACTIVE_ASPECTS.
|
|
119
|
-
Example:
|
|
120
|
-
[
|
|
121
|
-
{"name": "conjunction", "orb": 10},
|
|
122
|
-
{"name": "opposition", "orb": 10},
|
|
123
|
-
{"name": "trine", "orb": 8},
|
|
124
|
-
{"name": "sextile", "orb": 6},
|
|
125
|
-
{"name": "square", "orb": 5},
|
|
126
|
-
{"name": "quintile", "orb": 1},
|
|
127
|
-
]
|
|
128
|
-
|
|
129
|
-
Public Methods:
|
|
130
|
-
makeTemplate(minify=False, remove_css_variables=False) -> str:
|
|
131
|
-
Render the full chart SVG as a string without writing to disk. Use `minify=True`
|
|
132
|
-
to remove whitespace and quotes, and `remove_css_variables=True` to embed CSS vars.
|
|
133
|
-
|
|
134
|
-
makeSVG(minify=False, remove_css_variables=False) -> None:
|
|
135
|
-
Generate and write the full chart SVG file to the output directory.
|
|
136
|
-
Filenames follow the pattern:
|
|
137
|
-
'{subject.name} - {chart_type} Chart.svg'.
|
|
138
|
-
|
|
139
|
-
makeWheelOnlyTemplate(minify=False, remove_css_variables=False) -> str:
|
|
140
|
-
Render only the chart wheel (no aspect grid) as an SVG string.
|
|
141
|
-
|
|
142
|
-
makeWheelOnlySVG(minify=False, remove_css_variables=False) -> None:
|
|
143
|
-
Generate and write the wheel-only SVG file:
|
|
144
|
-
'{subject.name} - {chart_type} Chart - Wheel Only.svg'.
|
|
145
|
-
|
|
146
|
-
makeAspectGridOnlyTemplate(minify=False, remove_css_variables=False) -> str:
|
|
147
|
-
Render only the aspect grid as an SVG string.
|
|
148
|
-
|
|
149
|
-
makeAspectGridOnlySVG(minify=False, remove_css_variables=False) -> None:
|
|
150
|
-
Generate and write the aspect-grid-only SVG file:
|
|
151
|
-
'{subject.name} - {chart_type} Chart - Aspect Grid Only.svg'.
|
|
152
|
-
"""
|
|
153
|
-
|
|
154
|
-
# Constants
|
|
155
|
-
|
|
156
|
-
_DEFAULT_HEIGHT = 550
|
|
157
|
-
_DEFAULT_FULL_WIDTH = 1200
|
|
158
|
-
_DEFAULT_NATAL_WIDTH = 870
|
|
159
|
-
_DEFAULT_FULL_WIDTH_WITH_TABLE = 1200
|
|
160
|
-
_DEFAULT_ULTRA_WIDE_WIDTH = 1270
|
|
161
|
-
|
|
162
|
-
_BASIC_CHART_VIEWBOX = f"0 0 {_DEFAULT_NATAL_WIDTH} {_DEFAULT_HEIGHT}"
|
|
163
|
-
_WIDE_CHART_VIEWBOX = f"0 0 {_DEFAULT_FULL_WIDTH} 546.0"
|
|
164
|
-
_ULTRA_WIDE_CHART_VIEWBOX = f"0 0 {_DEFAULT_ULTRA_WIDE_WIDTH} 546.0"
|
|
165
|
-
_TRANSIT_CHART_WITH_TABLE_VIWBOX = f"0 0 {_DEFAULT_FULL_WIDTH_WITH_TABLE} 546.0"
|
|
166
|
-
|
|
167
|
-
# Set at init
|
|
168
|
-
first_obj: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel]
|
|
169
|
-
second_obj: Union[AstrologicalSubjectModel, PlanetReturnModel, None]
|
|
170
|
-
chart_type: ChartType
|
|
171
|
-
new_output_directory: Union[Path, None]
|
|
172
|
-
new_settings_file: Union[Path, None, KerykeionSettingsModel, dict]
|
|
173
|
-
output_directory: Path
|
|
174
|
-
new_settings_file: Union[Path, None, KerykeionSettingsModel, dict]
|
|
175
|
-
theme: Union[KerykeionChartTheme, None]
|
|
176
|
-
double_chart_aspect_grid_type: Literal["list", "table"]
|
|
177
|
-
chart_language: KerykeionChartLanguage
|
|
178
|
-
active_points: List[AstrologicalPoint]
|
|
179
|
-
active_aspects: List[ActiveAspect]
|
|
180
|
-
transparent_background: bool
|
|
181
|
-
|
|
182
|
-
# Internal properties
|
|
183
|
-
fire: float
|
|
184
|
-
earth: float
|
|
185
|
-
air: float
|
|
186
|
-
water: float
|
|
187
|
-
first_circle_radius: float
|
|
188
|
-
second_circle_radius: float
|
|
189
|
-
third_circle_radius: float
|
|
190
|
-
width: Union[float, int]
|
|
191
|
-
language_settings: dict
|
|
192
|
-
chart_colors_settings: dict
|
|
193
|
-
planets_settings: dict
|
|
194
|
-
aspects_settings: dict
|
|
195
|
-
available_planets_setting: List[KerykeionSettingsCelestialPointModel]
|
|
196
|
-
height: float
|
|
197
|
-
location: str
|
|
198
|
-
geolat: float
|
|
199
|
-
geolon: float
|
|
200
|
-
template: str
|
|
201
|
-
|
|
202
|
-
def __init__(
|
|
203
|
-
self,
|
|
204
|
-
first_obj: Union[AstrologicalSubjectModel, CompositeSubjectModel, PlanetReturnModel],
|
|
205
|
-
chart_type: ChartType = "Natal",
|
|
206
|
-
second_obj: Union[AstrologicalSubjectModel, PlanetReturnModel, None] = None,
|
|
207
|
-
new_output_directory: Union[str, None] = None,
|
|
208
|
-
new_settings_file: Union[Path, None, KerykeionSettingsModel, dict] = None,
|
|
209
|
-
theme: Union[KerykeionChartTheme, None] = "classic",
|
|
210
|
-
double_chart_aspect_grid_type: Literal["list", "table"] = "list",
|
|
211
|
-
chart_language: KerykeionChartLanguage = "EN",
|
|
212
|
-
active_points: Optional[list[AstrologicalPoint]] = None,
|
|
213
|
-
active_aspects: list[ActiveAspect]= DEFAULT_ACTIVE_ASPECTS,
|
|
214
|
-
*,
|
|
215
|
-
transparent_background: bool = False,
|
|
216
|
-
colors_settings: dict = DEFAULT_CHART_COLORS,
|
|
217
|
-
celestial_points_settings: list[dict] = DEFAULT_CELESTIAL_POINTS_SETTINGS,
|
|
218
|
-
aspects_settings: list[dict] = DEFAULT_CHART_ASPECTS_SETTINGS,
|
|
219
|
-
):
|
|
220
|
-
"""
|
|
221
|
-
Initialize the chart generator with subject data and configuration options.
|
|
222
|
-
|
|
223
|
-
Args:
|
|
224
|
-
first_obj (AstrologicalSubjectModel, or CompositeSubjectModel):
|
|
225
|
-
Primary astrological subject instance.
|
|
226
|
-
chart_type (ChartType, optional):
|
|
227
|
-
Type of chart to generate (e.g., 'Natal', 'Transit').
|
|
228
|
-
second_obj (AstrologicalSubject, optional):
|
|
229
|
-
Secondary subject for Transit or Synastry charts.
|
|
230
|
-
new_output_directory (str or Path, optional):
|
|
231
|
-
Base directory to save generated SVG files.
|
|
232
|
-
new_settings_file (Path, dict, or KerykeionSettingsModel, optional):
|
|
233
|
-
Custom settings source for chart colors, fonts, and aspects.
|
|
234
|
-
theme (KerykeionChartTheme or None, optional):
|
|
235
|
-
CSS theme to apply; None for default styling.
|
|
236
|
-
double_chart_aspect_grid_type (Literal['list','table'], optional):
|
|
237
|
-
Layout style for double-chart aspect grids ('list' or 'table').
|
|
238
|
-
chart_language (KerykeionChartLanguage, optional):
|
|
239
|
-
Language code for chart labels (e.g., 'EN', 'IT').
|
|
240
|
-
active_points (List[AstrologicalPoint], optional):
|
|
241
|
-
Celestial points to include in the chart visualization.
|
|
242
|
-
active_aspects (List[ActiveAspect], optional):
|
|
243
|
-
Aspects to calculate, each defined by name and orb.
|
|
244
|
-
transparent_background (bool, optional):
|
|
245
|
-
Whether to use a transparent background instead of the theme color. Defaults to False.
|
|
246
|
-
"""
|
|
247
|
-
# --------------------
|
|
248
|
-
# COMMON INITIALIZATION
|
|
249
|
-
# --------------------
|
|
250
|
-
home_directory = Path.home()
|
|
251
|
-
self.new_settings_file = new_settings_file
|
|
252
|
-
self.chart_language = chart_language
|
|
253
|
-
self.active_aspects = active_aspects
|
|
254
|
-
self.chart_type = chart_type
|
|
255
|
-
self.double_chart_aspect_grid_type = double_chart_aspect_grid_type
|
|
256
|
-
self.transparent_background = transparent_background
|
|
257
|
-
self.chart_colors_settings = colors_settings
|
|
258
|
-
self.planets_settings = celestial_points_settings
|
|
259
|
-
self.aspects_settings = aspects_settings
|
|
260
|
-
|
|
261
|
-
if not active_points:
|
|
262
|
-
self.active_points = first_obj.active_points
|
|
263
|
-
else:
|
|
264
|
-
self.active_points = find_common_active_points(
|
|
265
|
-
active_points,
|
|
266
|
-
first_obj.active_points
|
|
267
|
-
)
|
|
268
|
-
|
|
269
|
-
if second_obj:
|
|
270
|
-
self.active_points = find_common_active_points(
|
|
271
|
-
self.active_points,
|
|
272
|
-
second_obj.active_points
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
# Set output directory
|
|
276
|
-
if new_output_directory:
|
|
277
|
-
self.output_directory = Path(new_output_directory)
|
|
278
|
-
else:
|
|
279
|
-
self.output_directory = home_directory
|
|
280
|
-
|
|
281
|
-
# Load settings
|
|
282
|
-
self.parse_json_settings(new_settings_file)
|
|
283
|
-
|
|
284
|
-
# Primary subject
|
|
285
|
-
self.first_obj = first_obj
|
|
286
|
-
|
|
287
|
-
# Default radius for all charts
|
|
288
|
-
self.main_radius = 240
|
|
289
|
-
|
|
290
|
-
# Configure available planets
|
|
291
|
-
self.available_planets_setting = []
|
|
292
|
-
for body in self.planets_settings:
|
|
293
|
-
if body["name"] in self.active_points:
|
|
294
|
-
body["is_active"] = True
|
|
295
|
-
self.available_planets_setting.append(body)
|
|
296
|
-
|
|
297
|
-
# Set available celestial points
|
|
298
|
-
available_celestial_points_names = [body["name"].lower() for body in self.available_planets_setting]
|
|
299
|
-
self.available_kerykeion_celestial_points = []
|
|
300
|
-
for body in available_celestial_points_names:
|
|
301
|
-
self.available_kerykeion_celestial_points.append(self.first_obj.get(body))
|
|
302
|
-
|
|
303
|
-
# ------------------------
|
|
304
|
-
# CHART TYPE SPECIFIC SETUP
|
|
305
|
-
# ------------------------
|
|
306
|
-
|
|
307
|
-
if self.chart_type in ["Natal", "ExternalNatal"]:
|
|
308
|
-
# --- NATAL / EXTERNAL NATAL CHART SETUP ---
|
|
309
|
-
|
|
310
|
-
# Validate Subject
|
|
311
|
-
if not isinstance(self.first_obj, AstrologicalSubjectModel):
|
|
312
|
-
raise KerykeionException("First object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
|
|
313
|
-
|
|
314
|
-
# Calculate aspects
|
|
315
|
-
natal_aspects_instance = NatalAspects(
|
|
316
|
-
self.first_obj,
|
|
317
|
-
new_settings_file=self.new_settings_file,
|
|
318
|
-
active_points=self.active_points,
|
|
319
|
-
active_aspects=active_aspects,
|
|
320
|
-
)
|
|
321
|
-
self.aspects_list = natal_aspects_instance.relevant_aspects
|
|
322
|
-
|
|
323
|
-
# Screen size
|
|
324
|
-
self.height = self._DEFAULT_HEIGHT
|
|
325
|
-
self.width = self._DEFAULT_NATAL_WIDTH
|
|
326
|
-
|
|
327
|
-
# Location and coordinates
|
|
328
|
-
self.location = self.first_obj.city
|
|
329
|
-
self.geolat = self.first_obj.lat
|
|
330
|
-
self.geolon = self.first_obj.lng
|
|
331
|
-
|
|
332
|
-
# Circle radii
|
|
333
|
-
if self.chart_type == "ExternalNatal":
|
|
334
|
-
self.first_circle_radius = 56
|
|
335
|
-
self.second_circle_radius = 92
|
|
336
|
-
self.third_circle_radius = 112
|
|
337
|
-
else:
|
|
338
|
-
self.first_circle_radius = 0
|
|
339
|
-
self.second_circle_radius = 36
|
|
340
|
-
self.third_circle_radius = 120
|
|
341
|
-
|
|
342
|
-
elif self.chart_type == "Composite":
|
|
343
|
-
# --- COMPOSITE CHART SETUP ---
|
|
344
|
-
|
|
345
|
-
# Validate Subject
|
|
346
|
-
if not isinstance(self.first_obj, CompositeSubjectModel):
|
|
347
|
-
raise KerykeionException("First object must be a CompositeSubjectModel instance.")
|
|
348
|
-
|
|
349
|
-
# Calculate aspects
|
|
350
|
-
self.aspects_list = NatalAspects(self.first_obj, new_settings_file=self.new_settings_file, active_points=self.active_points).relevant_aspects
|
|
351
|
-
|
|
352
|
-
# Screen size
|
|
353
|
-
self.height = self._DEFAULT_HEIGHT
|
|
354
|
-
self.width = self._DEFAULT_NATAL_WIDTH
|
|
355
|
-
|
|
356
|
-
# Location and coordinates (average of both subjects)
|
|
357
|
-
self.location = ""
|
|
358
|
-
self.geolat = (self.first_obj.first_subject.lat + self.first_obj.second_subject.lat) / 2
|
|
359
|
-
self.geolon = (self.first_obj.first_subject.lng + self.first_obj.second_subject.lng) / 2
|
|
360
|
-
|
|
361
|
-
# Circle radii
|
|
362
|
-
self.first_circle_radius = 0
|
|
363
|
-
self.second_circle_radius = 36
|
|
364
|
-
self.third_circle_radius = 120
|
|
365
|
-
|
|
366
|
-
elif self.chart_type == "Transit":
|
|
367
|
-
# --- TRANSIT CHART SETUP ---
|
|
368
|
-
|
|
369
|
-
# Validate Subjects
|
|
370
|
-
if not second_obj:
|
|
371
|
-
raise KerykeionException("Second object is required for Transit charts.")
|
|
372
|
-
if not isinstance(self.first_obj, AstrologicalSubjectModel):
|
|
373
|
-
raise KerykeionException("First object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
|
|
374
|
-
if not isinstance(second_obj, AstrologicalSubjectModel):
|
|
375
|
-
raise KerykeionException("Second object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
|
|
376
|
-
|
|
377
|
-
# Secondary subject setup
|
|
378
|
-
self.second_obj = second_obj
|
|
379
|
-
|
|
380
|
-
# Calculate aspects (transit to natal)
|
|
381
|
-
synastry_aspects_instance = SynastryAspects(
|
|
382
|
-
self.first_obj,
|
|
383
|
-
self.second_obj,
|
|
384
|
-
new_settings_file=self.new_settings_file,
|
|
385
|
-
active_points=self.active_points,
|
|
386
|
-
active_aspects=active_aspects,
|
|
387
|
-
)
|
|
388
|
-
self.aspects_list = synastry_aspects_instance.relevant_aspects
|
|
389
|
-
|
|
390
|
-
# Secondary subject available points
|
|
391
|
-
self.t_available_kerykeion_celestial_points = self.available_kerykeion_celestial_points
|
|
392
|
-
|
|
393
|
-
# Screen size
|
|
394
|
-
self.height = self._DEFAULT_HEIGHT
|
|
395
|
-
if self.double_chart_aspect_grid_type == "table":
|
|
396
|
-
self.width = self._DEFAULT_FULL_WIDTH_WITH_TABLE
|
|
397
|
-
else:
|
|
398
|
-
self.width = self._DEFAULT_FULL_WIDTH
|
|
399
|
-
|
|
400
|
-
# Location and coordinates (from transit subject)
|
|
401
|
-
self.location = self.second_obj.city
|
|
402
|
-
self.geolat = self.second_obj.lat
|
|
403
|
-
self.geolon = self.second_obj.lng
|
|
404
|
-
self.t_name = self.language_settings["transit_name"]
|
|
405
|
-
|
|
406
|
-
# Circle radii
|
|
407
|
-
self.first_circle_radius = 0
|
|
408
|
-
self.second_circle_radius = 36
|
|
409
|
-
self.third_circle_radius = 120
|
|
410
|
-
|
|
411
|
-
elif self.chart_type == "Synastry":
|
|
412
|
-
# --- SYNASTRY CHART SETUP ---
|
|
413
|
-
|
|
414
|
-
# Validate Subjects
|
|
415
|
-
if not second_obj:
|
|
416
|
-
raise KerykeionException("Second object is required for Synastry charts.")
|
|
417
|
-
if not isinstance(self.first_obj, AstrologicalSubjectModel):
|
|
418
|
-
raise KerykeionException("First object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
|
|
419
|
-
if not isinstance(second_obj, AstrologicalSubjectModel):
|
|
420
|
-
raise KerykeionException("Second object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
|
|
421
|
-
|
|
422
|
-
# Secondary subject setup
|
|
423
|
-
self.second_obj = second_obj
|
|
424
|
-
|
|
425
|
-
# Calculate aspects (natal to partner)
|
|
426
|
-
synastry_aspects_instance = SynastryAspects(
|
|
427
|
-
self.first_obj,
|
|
428
|
-
self.second_obj,
|
|
429
|
-
new_settings_file=self.new_settings_file,
|
|
430
|
-
active_points=self.active_points,
|
|
431
|
-
active_aspects=active_aspects,
|
|
432
|
-
)
|
|
433
|
-
self.aspects_list = synastry_aspects_instance.relevant_aspects
|
|
434
|
-
|
|
435
|
-
# Secondary subject available points
|
|
436
|
-
self.t_available_kerykeion_celestial_points = self.available_kerykeion_celestial_points
|
|
437
|
-
|
|
438
|
-
# Screen size
|
|
439
|
-
self.height = self._DEFAULT_HEIGHT
|
|
440
|
-
self.width = self._DEFAULT_FULL_WIDTH
|
|
441
|
-
|
|
442
|
-
# Location and coordinates (from primary subject)
|
|
443
|
-
self.location = self.first_obj.city
|
|
444
|
-
self.geolat = self.first_obj.lat
|
|
445
|
-
self.geolon = self.first_obj.lng
|
|
446
|
-
|
|
447
|
-
# Circle radii
|
|
448
|
-
self.first_circle_radius = 0
|
|
449
|
-
self.second_circle_radius = 36
|
|
450
|
-
self.third_circle_radius = 120
|
|
451
|
-
|
|
452
|
-
elif self.chart_type == "Return":
|
|
453
|
-
# --- RETURN CHART SETUP ---
|
|
454
|
-
|
|
455
|
-
# Validate Subjects
|
|
456
|
-
if not second_obj:
|
|
457
|
-
raise KerykeionException("Second object is required for Return charts.")
|
|
458
|
-
if not isinstance(self.first_obj, AstrologicalSubjectModel):
|
|
459
|
-
raise KerykeionException("First object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
|
|
460
|
-
if not isinstance(second_obj, PlanetReturnModel):
|
|
461
|
-
raise KerykeionException("Second object must be a PlanetReturnModel instance.")
|
|
462
|
-
|
|
463
|
-
# Secondary subject setup
|
|
464
|
-
self.second_obj = second_obj
|
|
465
|
-
|
|
466
|
-
# Calculate aspects (natal to return)
|
|
467
|
-
synastry_aspects_instance = SynastryAspects(
|
|
468
|
-
self.first_obj,
|
|
469
|
-
self.second_obj,
|
|
470
|
-
new_settings_file=self.new_settings_file,
|
|
471
|
-
active_points=self.active_points,
|
|
472
|
-
active_aspects=active_aspects,
|
|
473
|
-
)
|
|
474
|
-
self.aspects_list = synastry_aspects_instance.relevant_aspects
|
|
475
|
-
|
|
476
|
-
# Secondary subject available points
|
|
477
|
-
self.t_available_kerykeion_celestial_points = self.available_kerykeion_celestial_points
|
|
478
|
-
|
|
479
|
-
# Screen size
|
|
480
|
-
self.height = self._DEFAULT_HEIGHT
|
|
481
|
-
self.width = self._DEFAULT_ULTRA_WIDE_WIDTH
|
|
482
|
-
|
|
483
|
-
# Location and coordinates (from natal subject)
|
|
484
|
-
self.location = self.first_obj.city
|
|
485
|
-
self.geolat = self.first_obj.lat
|
|
486
|
-
self.geolon = self.first_obj.lng
|
|
487
|
-
|
|
488
|
-
# Circle radii
|
|
489
|
-
self.first_circle_radius = 0
|
|
490
|
-
self.second_circle_radius = 36
|
|
491
|
-
self.third_circle_radius = 120
|
|
492
|
-
|
|
493
|
-
elif self.chart_type == "SingleWheelReturn":
|
|
494
|
-
# --- NATAL / EXTERNAL NATAL CHART SETUP ---
|
|
495
|
-
|
|
496
|
-
# Validate Subject
|
|
497
|
-
if not isinstance(self.first_obj, PlanetReturnModel):
|
|
498
|
-
raise KerykeionException("First object must be an AstrologicalSubjectModel or AstrologicalSubject instance.")
|
|
499
|
-
|
|
500
|
-
# Calculate aspects
|
|
501
|
-
natal_aspects_instance = NatalAspects(
|
|
502
|
-
self.first_obj,
|
|
503
|
-
new_settings_file=self.new_settings_file,
|
|
504
|
-
active_points=self.active_points,
|
|
505
|
-
active_aspects=active_aspects,
|
|
506
|
-
)
|
|
507
|
-
self.aspects_list = natal_aspects_instance.relevant_aspects
|
|
508
|
-
|
|
509
|
-
# Screen size
|
|
510
|
-
self.height = self._DEFAULT_HEIGHT
|
|
511
|
-
self.width = self._DEFAULT_NATAL_WIDTH
|
|
512
|
-
|
|
513
|
-
# Location and coordinates
|
|
514
|
-
self.location = self.first_obj.city
|
|
515
|
-
self.geolat = self.first_obj.lat
|
|
516
|
-
self.geolon = self.first_obj.lng
|
|
517
|
-
|
|
518
|
-
# Circle radii
|
|
519
|
-
if self.chart_type == "ExternalNatal":
|
|
520
|
-
self.first_circle_radius = 56
|
|
521
|
-
self.second_circle_radius = 92
|
|
522
|
-
self.third_circle_radius = 112
|
|
523
|
-
else:
|
|
524
|
-
self.first_circle_radius = 0
|
|
525
|
-
self.second_circle_radius = 36
|
|
526
|
-
self.third_circle_radius = 120
|
|
527
|
-
|
|
528
|
-
# --------------------
|
|
529
|
-
# FINAL COMMON SETUP
|
|
530
|
-
# --------------------
|
|
531
|
-
|
|
532
|
-
# Calculate element points
|
|
533
|
-
celestial_points_names = [body["name"].lower() for body in self.available_planets_setting]
|
|
534
|
-
if self.chart_type == "Synastry":
|
|
535
|
-
element_totals = calculate_synastry_element_points(
|
|
536
|
-
self.available_planets_setting,
|
|
537
|
-
celestial_points_names,
|
|
538
|
-
self.first_obj,
|
|
539
|
-
self.second_obj,
|
|
540
|
-
)
|
|
541
|
-
else:
|
|
542
|
-
element_totals = calculate_element_points(
|
|
543
|
-
self.available_planets_setting,
|
|
544
|
-
celestial_points_names,
|
|
545
|
-
self.first_obj,
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
self.fire = element_totals["fire"]
|
|
549
|
-
self.earth = element_totals["earth"]
|
|
550
|
-
self.air = element_totals["air"]
|
|
551
|
-
self.water = element_totals["water"]
|
|
552
|
-
|
|
553
|
-
# Calculate qualities points
|
|
554
|
-
if self.chart_type == "Synastry":
|
|
555
|
-
qualities_totals = calculate_synastry_quality_points(
|
|
556
|
-
self.available_planets_setting,
|
|
557
|
-
celestial_points_names,
|
|
558
|
-
self.first_obj,
|
|
559
|
-
self.second_obj,
|
|
560
|
-
)
|
|
561
|
-
else:
|
|
562
|
-
qualities_totals = calculate_quality_points(
|
|
563
|
-
self.available_planets_setting,
|
|
564
|
-
celestial_points_names,
|
|
565
|
-
self.first_obj,
|
|
566
|
-
)
|
|
567
|
-
|
|
568
|
-
self.cardinal = qualities_totals["cardinal"]
|
|
569
|
-
self.fixed = qualities_totals["fixed"]
|
|
570
|
-
self.mutable = qualities_totals["mutable"]
|
|
571
|
-
|
|
572
|
-
# Set up theme
|
|
573
|
-
if theme not in get_args(KerykeionChartTheme) and theme is not None:
|
|
574
|
-
raise KerykeionException(f"Theme {theme} is not available. Set None for default theme.")
|
|
575
|
-
|
|
576
|
-
self.set_up_theme(theme)
|
|
577
|
-
|
|
578
|
-
def set_up_theme(self, theme: Union[KerykeionChartTheme, None] = None) -> None:
|
|
579
|
-
"""
|
|
580
|
-
Load and apply a CSS theme for the chart visualization.
|
|
581
|
-
|
|
582
|
-
Args:
|
|
583
|
-
theme (KerykeionChartTheme or None): Name of the theme to apply. If None, no CSS is applied.
|
|
584
|
-
"""
|
|
585
|
-
if theme is None:
|
|
586
|
-
self.color_style_tag = ""
|
|
587
|
-
return
|
|
588
|
-
|
|
589
|
-
theme_dir = Path(__file__).parent / "themes"
|
|
590
|
-
|
|
591
|
-
with open(theme_dir / f"{theme}.css", "r") as f:
|
|
592
|
-
self.color_style_tag = f.read()
|
|
593
|
-
|
|
594
|
-
def set_output_directory(self, dir_path: Path) -> None:
|
|
595
|
-
"""
|
|
596
|
-
Set the directory where generated SVG files will be saved.
|
|
597
|
-
|
|
598
|
-
Args:
|
|
599
|
-
dir_path (Path): Target directory for SVG output.
|
|
600
|
-
"""
|
|
601
|
-
self.output_directory = dir_path
|
|
602
|
-
logging.info(f"Output directory set to: {self.output_directory}")
|
|
603
|
-
|
|
604
|
-
def parse_json_settings(self, settings_file_or_dict: Union[Path, dict, KerykeionSettingsModel, None]) -> None:
|
|
605
|
-
"""
|
|
606
|
-
Load and parse chart configuration settings.
|
|
607
|
-
|
|
608
|
-
Args:
|
|
609
|
-
settings_file_or_dict (Path, dict, or KerykeionSettingsModel):
|
|
610
|
-
Source for custom chart settings.
|
|
611
|
-
"""
|
|
612
|
-
settings = get_settings(settings_file_or_dict)
|
|
613
|
-
|
|
614
|
-
self.language_settings = settings["language_settings"][self.chart_language]
|
|
615
|
-
|
|
616
|
-
def _draw_zodiac_circle_slices(self, r):
|
|
617
|
-
"""
|
|
618
|
-
Draw zodiac circle slices for each sign.
|
|
619
|
-
|
|
620
|
-
Args:
|
|
621
|
-
r (float): Outer radius of the zodiac ring.
|
|
622
|
-
|
|
623
|
-
Returns:
|
|
624
|
-
str: Concatenated SVG elements for zodiac slices.
|
|
625
|
-
"""
|
|
626
|
-
sings = get_args(Sign)
|
|
627
|
-
output = ""
|
|
628
|
-
for i, sing in enumerate(sings):
|
|
629
|
-
output += draw_zodiac_slice(
|
|
630
|
-
c1=self.first_circle_radius,
|
|
631
|
-
chart_type=self.chart_type,
|
|
632
|
-
seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
633
|
-
num=i,
|
|
634
|
-
r=r,
|
|
635
|
-
style=f'fill:{self.chart_colors_settings[f"zodiac_bg_{i}"]}; fill-opacity: 0.5;',
|
|
636
|
-
type=sing,
|
|
637
|
-
)
|
|
638
|
-
|
|
639
|
-
return output
|
|
640
|
-
|
|
641
|
-
def _draw_all_aspects_lines(self, r, ar):
|
|
642
|
-
"""
|
|
643
|
-
Render SVG lines for all aspects in the chart.
|
|
644
|
-
|
|
645
|
-
Args:
|
|
646
|
-
r (float): Radius at which aspect lines originate.
|
|
647
|
-
ar (float): Radius at which aspect lines terminate.
|
|
648
|
-
|
|
649
|
-
Returns:
|
|
650
|
-
str: SVG markup for all aspect lines.
|
|
651
|
-
"""
|
|
652
|
-
out = ""
|
|
653
|
-
for aspect in self.aspects_list:
|
|
654
|
-
aspect_name = aspect["aspect"]
|
|
655
|
-
aspect_color = next((a["color"] for a in self.aspects_settings if a["name"] == aspect_name), None)
|
|
656
|
-
if aspect_color:
|
|
657
|
-
out += draw_aspect_line(
|
|
658
|
-
r=r,
|
|
659
|
-
ar=ar,
|
|
660
|
-
aspect=aspect,
|
|
661
|
-
color=aspect_color,
|
|
662
|
-
seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
663
|
-
)
|
|
664
|
-
return out
|
|
665
|
-
|
|
666
|
-
def _draw_all_transit_aspects_lines(self, r, ar):
|
|
667
|
-
"""
|
|
668
|
-
Render SVG lines for all transit aspects in the chart.
|
|
669
|
-
|
|
670
|
-
Args:
|
|
671
|
-
r (float): Radius at which transit aspect lines originate.
|
|
672
|
-
ar (float): Radius at which transit aspect lines terminate.
|
|
673
|
-
|
|
674
|
-
Returns:
|
|
675
|
-
str: SVG markup for all transit aspect lines.
|
|
676
|
-
"""
|
|
677
|
-
out = ""
|
|
678
|
-
for aspect in self.aspects_list:
|
|
679
|
-
aspect_name = aspect["aspect"]
|
|
680
|
-
aspect_color = next((a["color"] for a in self.aspects_settings if a["name"] == aspect_name), None)
|
|
681
|
-
if aspect_color:
|
|
682
|
-
out += draw_aspect_line(
|
|
683
|
-
r=r,
|
|
684
|
-
ar=ar,
|
|
685
|
-
aspect=aspect,
|
|
686
|
-
color=aspect_color,
|
|
687
|
-
seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
688
|
-
)
|
|
689
|
-
return out
|
|
690
|
-
|
|
691
|
-
def _create_template_dictionary(self) -> ChartTemplateDictionary:
|
|
692
|
-
"""
|
|
693
|
-
Assemble chart data and rendering instructions into a template dictionary.
|
|
694
|
-
|
|
695
|
-
Gathers styling, dimensions, and SVG fragments for chart components based on
|
|
696
|
-
chart type and subjects.
|
|
697
|
-
|
|
698
|
-
Returns:
|
|
699
|
-
ChartTemplateDictionary: Populated structure of template variables.
|
|
700
|
-
"""
|
|
701
|
-
# Initialize template dictionary
|
|
702
|
-
template_dict: dict = {}
|
|
703
|
-
|
|
704
|
-
# -------------------------------------#
|
|
705
|
-
# COMMON SETTINGS FOR ALL CHART TYPES #
|
|
706
|
-
# -------------------------------------#
|
|
707
|
-
|
|
708
|
-
# Set the color style tag and basic dimensions
|
|
709
|
-
template_dict["color_style_tag"] = self.color_style_tag
|
|
710
|
-
template_dict["chart_height"] = self.height
|
|
711
|
-
template_dict["chart_width"] = self.width
|
|
712
|
-
|
|
713
|
-
# Set paper colors
|
|
714
|
-
template_dict["paper_color_0"] = self.chart_colors_settings["paper_0"]
|
|
715
|
-
template_dict["paper_color_1"] = self.chart_colors_settings["paper_1"]
|
|
716
|
-
|
|
717
|
-
# Set background color based on transparent_background setting
|
|
718
|
-
if self.transparent_background:
|
|
719
|
-
template_dict["background_color"] = "transparent"
|
|
720
|
-
else:
|
|
721
|
-
template_dict["background_color"] = self.chart_colors_settings["paper_1"]
|
|
722
|
-
|
|
723
|
-
# Set planet colors
|
|
724
|
-
for planet in self.planets_settings:
|
|
725
|
-
planet_id = planet["id"]
|
|
726
|
-
template_dict[f"planets_color_{planet_id}"] = planet["color"]
|
|
727
|
-
|
|
728
|
-
# Set zodiac colors
|
|
729
|
-
for i in range(12):
|
|
730
|
-
template_dict[f"zodiac_color_{i}"] = self.chart_colors_settings[f"zodiac_icon_{i}"]
|
|
731
|
-
|
|
732
|
-
# Set orb colors
|
|
733
|
-
for aspect in self.aspects_settings:
|
|
734
|
-
template_dict[f"orb_color_{aspect['degree']}"] = aspect["color"]
|
|
735
|
-
|
|
736
|
-
# Draw zodiac circle slices
|
|
737
|
-
template_dict["makeZodiac"] = self._draw_zodiac_circle_slices(self.main_radius)
|
|
738
|
-
|
|
739
|
-
# Calculate element percentages
|
|
740
|
-
total_elements = self.fire + self.water + self.earth + self.air
|
|
741
|
-
fire_percentage = int(round(100 * self.fire / total_elements))
|
|
742
|
-
earth_percentage = int(round(100 * self.earth / total_elements))
|
|
743
|
-
air_percentage = int(round(100 * self.air / total_elements))
|
|
744
|
-
water_percentage = int(round(100 * self.water / total_elements))
|
|
745
|
-
|
|
746
|
-
# Element Percentages
|
|
747
|
-
template_dict["elements_string"] = f"{self.language_settings.get('elements', 'Elements')}:"
|
|
748
|
-
template_dict["fire_string"] = f"{self.language_settings['fire']} {fire_percentage}%"
|
|
749
|
-
template_dict["earth_string"] = f"{self.language_settings['earth']} {earth_percentage}%"
|
|
750
|
-
template_dict["air_string"] = f"{self.language_settings['air']} {air_percentage}%"
|
|
751
|
-
template_dict["water_string"] = f"{self.language_settings['water']} {water_percentage}%"
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
# Qualities Percentages
|
|
755
|
-
total_qualities = self.cardinal + self.fixed + self.mutable
|
|
756
|
-
cardinal_percentage = int(round(100 * self.cardinal / total_qualities))
|
|
757
|
-
fixed_percentage = int(round(100 * self.fixed / total_qualities))
|
|
758
|
-
mutable_percentage = int(round(100 * self.mutable / total_qualities))
|
|
759
|
-
|
|
760
|
-
template_dict["qualities_string"] = f"{self.language_settings.get('qualities', 'Qualities')}:"
|
|
761
|
-
template_dict["cardinal_string"] = f"{self.language_settings.get('cardinal', 'Cardinal')} {cardinal_percentage}%"
|
|
762
|
-
template_dict["fixed_string"] = f"{self.language_settings.get('fixed', 'Fixed')} {fixed_percentage}%"
|
|
763
|
-
template_dict["mutable_string"] = f"{self.language_settings.get('mutable', 'Mutable')} {mutable_percentage}%"
|
|
764
|
-
|
|
765
|
-
# Get houses list for main subject
|
|
766
|
-
first_subject_houses_list = get_houses_list(self.first_obj)
|
|
767
|
-
|
|
768
|
-
# ------------------------------- #
|
|
769
|
-
# CHART TYPE SPECIFIC SETTINGS #
|
|
770
|
-
# ------------------------------- #
|
|
771
|
-
|
|
772
|
-
if self.chart_type in ["Natal", "ExternalNatal"]:
|
|
773
|
-
# Set viewbox
|
|
774
|
-
template_dict["viewbox"] = self._BASIC_CHART_VIEWBOX
|
|
775
|
-
|
|
776
|
-
# Rings and circles
|
|
777
|
-
template_dict["transitRing"] = ""
|
|
778
|
-
template_dict["degreeRing"] = draw_degree_ring(
|
|
779
|
-
self.main_radius,
|
|
780
|
-
self.first_circle_radius,
|
|
781
|
-
self.first_obj.seventh_house.abs_pos,
|
|
782
|
-
self.chart_colors_settings["paper_0"],
|
|
783
|
-
)
|
|
784
|
-
template_dict["background_circle"] = draw_background_circle(
|
|
785
|
-
self.main_radius,
|
|
786
|
-
self.chart_colors_settings["paper_1"],
|
|
787
|
-
self.chart_colors_settings["paper_1"],
|
|
788
|
-
)
|
|
789
|
-
template_dict["first_circle"] = draw_first_circle(
|
|
790
|
-
self.main_radius,
|
|
791
|
-
self.chart_colors_settings["zodiac_radix_ring_2"],
|
|
792
|
-
self.chart_type,
|
|
793
|
-
self.first_circle_radius,
|
|
794
|
-
)
|
|
795
|
-
template_dict["second_circle"] = draw_second_circle(
|
|
796
|
-
self.main_radius,
|
|
797
|
-
self.chart_colors_settings["zodiac_radix_ring_1"],
|
|
798
|
-
self.chart_colors_settings["paper_1"],
|
|
799
|
-
self.chart_type,
|
|
800
|
-
self.second_circle_radius,
|
|
801
|
-
)
|
|
802
|
-
template_dict["third_circle"] = draw_third_circle(
|
|
803
|
-
self.main_radius,
|
|
804
|
-
self.chart_colors_settings["zodiac_radix_ring_0"],
|
|
805
|
-
self.chart_colors_settings["paper_1"],
|
|
806
|
-
self.chart_type,
|
|
807
|
-
self.third_circle_radius,
|
|
808
|
-
)
|
|
809
|
-
|
|
810
|
-
# Aspects
|
|
811
|
-
template_dict["makeDoubleChartAspectList"] = ""
|
|
812
|
-
template_dict["makeAspectGrid"] = draw_aspect_grid(
|
|
813
|
-
self.chart_colors_settings["paper_0"],
|
|
814
|
-
self.available_planets_setting,
|
|
815
|
-
self.aspects_list,
|
|
816
|
-
)
|
|
817
|
-
template_dict["makeAspects"] = self._draw_all_aspects_lines(self.main_radius, self.main_radius - self.third_circle_radius)
|
|
818
|
-
|
|
819
|
-
# Chart title
|
|
820
|
-
template_dict["stringTitle"] = f'{self.first_obj.name} - {self.language_settings.get("Birth Chart", "Birth Chart")}'
|
|
821
|
-
|
|
822
|
-
# Top left section
|
|
823
|
-
latitude_string = convert_latitude_coordinate_to_string(self.geolat, self.language_settings["north"], self.language_settings["south"])
|
|
824
|
-
longitude_string = convert_longitude_coordinate_to_string(self.geolon, self.language_settings["east"], self.language_settings["west"])
|
|
825
|
-
|
|
826
|
-
template_dict["top_left_0"] = f'{self.language_settings.get("location", "Location")}:'
|
|
827
|
-
template_dict["top_left_1"] = f"{self.first_obj.city}, {self.first_obj.nation}"
|
|
828
|
-
template_dict["top_left_2"] = f"{self.language_settings['latitude']}: {latitude_string}"
|
|
829
|
-
template_dict["top_left_3"] = f"{self.language_settings['longitude']}: {longitude_string}"
|
|
830
|
-
template_dict["top_left_4"] = format_datetime_with_timezone(self.first_obj.iso_formatted_local_datetime) # type: ignore
|
|
831
|
-
template_dict["top_left_5"] = f"{self.language_settings.get('day_of_week', 'Day of Week')}: {self.first_obj.day_of_week}" # type: ignore
|
|
832
|
-
|
|
833
|
-
# Bottom left section
|
|
834
|
-
if self.first_obj.zodiac_type == "Tropic":
|
|
835
|
-
zodiac_info = f"{self.language_settings.get('zodiac', 'Zodiac')}: {self.language_settings.get('tropical', 'Tropical')}"
|
|
836
|
-
else:
|
|
837
|
-
mode_const = "SIDM_" + self.first_obj.sidereal_mode # type: ignore
|
|
838
|
-
mode_name = swe.get_ayanamsa_name(getattr(swe, mode_const))
|
|
839
|
-
zodiac_info = f"{self.language_settings.get('ayanamsa', 'Ayanamsa')}: {mode_name}"
|
|
840
|
-
|
|
841
|
-
template_dict["bottom_left_0"] = zodiac_info
|
|
842
|
-
template_dict["bottom_left_1"] = f"{self.language_settings.get('domification', 'Domification')}: {self.language_settings.get('houses_system_' + self.first_obj.houses_system_identifier, self.first_obj.houses_system_name)}"
|
|
843
|
-
|
|
844
|
-
# Lunar phase information (optional)
|
|
845
|
-
if self.first_obj.lunar_phase is not None:
|
|
846
|
-
template_dict["bottom_left_2"] = f'{self.language_settings.get("lunation_day", "Lunation Day")}: {self.first_obj.lunar_phase.get("moon_phase", "")}'
|
|
847
|
-
template_dict["bottom_left_3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get(self.first_obj.lunar_phase.moon_phase_name.lower().replace(" ", "_"), self.first_obj.lunar_phase.moon_phase_name)}'
|
|
848
|
-
else:
|
|
849
|
-
template_dict["bottom_left_2"] = ""
|
|
850
|
-
template_dict["bottom_left_3"] = ""
|
|
851
|
-
|
|
852
|
-
template_dict["bottom_left_4"] = f'{self.language_settings.get("perspective_type", "Perspective")}: {self.language_settings.get(self.first_obj.perspective_type.lower().replace(" ", "_"), self.first_obj.perspective_type)}'
|
|
853
|
-
|
|
854
|
-
# Moon phase section calculations
|
|
855
|
-
if self.first_obj.lunar_phase is not None:
|
|
856
|
-
template_dict["makeLunarPhase"] = makeLunarPhase(self.first_obj.lunar_phase["degrees_between_s_m"], self.geolat)
|
|
857
|
-
else:
|
|
858
|
-
template_dict["makeLunarPhase"] = ""
|
|
859
|
-
|
|
860
|
-
# Houses and planet drawing
|
|
861
|
-
template_dict["makeMainHousesGrid"] = draw_main_house_grid(
|
|
862
|
-
main_subject_houses_list=first_subject_houses_list,
|
|
863
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
864
|
-
house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
865
|
-
)
|
|
866
|
-
template_dict["makeSecondaryHousesGrid"] = ""
|
|
867
|
-
|
|
868
|
-
template_dict["makeHouses"] = draw_houses_cusps_and_text_number(
|
|
869
|
-
r=self.main_radius,
|
|
870
|
-
first_subject_houses_list=first_subject_houses_list,
|
|
871
|
-
standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
|
|
872
|
-
first_house_color=self.planets_settings[12]["color"],
|
|
873
|
-
tenth_house_color=self.planets_settings[13]["color"],
|
|
874
|
-
seventh_house_color=self.planets_settings[14]["color"],
|
|
875
|
-
fourth_house_color=self.planets_settings[15]["color"],
|
|
876
|
-
c1=self.first_circle_radius,
|
|
877
|
-
c3=self.third_circle_radius,
|
|
878
|
-
chart_type=self.chart_type,
|
|
879
|
-
)
|
|
880
|
-
|
|
881
|
-
template_dict["makePlanets"] = draw_planets(
|
|
882
|
-
available_planets_setting=self.available_planets_setting,
|
|
883
|
-
chart_type=self.chart_type,
|
|
884
|
-
radius=self.main_radius,
|
|
885
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
886
|
-
third_circle_radius=self.third_circle_radius,
|
|
887
|
-
main_subject_first_house_degree_ut=self.first_obj.first_house.abs_pos,
|
|
888
|
-
main_subject_seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
889
|
-
)
|
|
890
|
-
|
|
891
|
-
template_dict["makeMainPlanetGrid"] = draw_main_planet_grid(
|
|
892
|
-
planets_and_houses_grid_title=self.language_settings["planets_and_house"],
|
|
893
|
-
subject_name=self.first_obj.name,
|
|
894
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
895
|
-
chart_type=self.chart_type,
|
|
896
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
897
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
898
|
-
)
|
|
899
|
-
template_dict["makeSecondaryPlanetGrid"] = ""
|
|
900
|
-
template_dict["makeHouseComparisonGrid"] = ""
|
|
901
|
-
|
|
902
|
-
elif self.chart_type == "Composite":
|
|
903
|
-
# Set viewbox
|
|
904
|
-
template_dict["viewbox"] = self._BASIC_CHART_VIEWBOX
|
|
905
|
-
|
|
906
|
-
# Rings and circles
|
|
907
|
-
template_dict["transitRing"] = ""
|
|
908
|
-
template_dict["degreeRing"] = draw_degree_ring(
|
|
909
|
-
self.main_radius,
|
|
910
|
-
self.first_circle_radius,
|
|
911
|
-
self.first_obj.seventh_house.abs_pos,
|
|
912
|
-
self.chart_colors_settings["paper_0"],
|
|
913
|
-
)
|
|
914
|
-
template_dict["background_circle"] = draw_background_circle(
|
|
915
|
-
self.main_radius,
|
|
916
|
-
self.chart_colors_settings["paper_1"],
|
|
917
|
-
self.chart_colors_settings["paper_1"],
|
|
918
|
-
)
|
|
919
|
-
template_dict["first_circle"] = draw_first_circle(
|
|
920
|
-
self.main_radius,
|
|
921
|
-
self.chart_colors_settings["zodiac_radix_ring_2"],
|
|
922
|
-
self.chart_type,
|
|
923
|
-
self.first_circle_radius,
|
|
924
|
-
)
|
|
925
|
-
template_dict["second_circle"] = draw_second_circle(
|
|
926
|
-
self.main_radius,
|
|
927
|
-
self.chart_colors_settings["zodiac_radix_ring_1"],
|
|
928
|
-
self.chart_colors_settings["paper_1"],
|
|
929
|
-
self.chart_type,
|
|
930
|
-
self.second_circle_radius,
|
|
931
|
-
)
|
|
932
|
-
template_dict["third_circle"] = draw_third_circle(
|
|
933
|
-
self.main_radius,
|
|
934
|
-
self.chart_colors_settings["zodiac_radix_ring_0"],
|
|
935
|
-
self.chart_colors_settings["paper_1"],
|
|
936
|
-
self.chart_type,
|
|
937
|
-
self.third_circle_radius,
|
|
938
|
-
)
|
|
939
|
-
|
|
940
|
-
# Aspects
|
|
941
|
-
template_dict["makeDoubleChartAspectList"] = ""
|
|
942
|
-
template_dict["makeAspectGrid"] = draw_aspect_grid(
|
|
943
|
-
self.chart_colors_settings["paper_0"],
|
|
944
|
-
self.available_planets_setting,
|
|
945
|
-
self.aspects_list,
|
|
946
|
-
)
|
|
947
|
-
template_dict["makeAspects"] = self._draw_all_aspects_lines(self.main_radius, self.main_radius - self.third_circle_radius)
|
|
948
|
-
|
|
949
|
-
# Chart title
|
|
950
|
-
template_dict["stringTitle"] = f"{self.first_obj.first_subject.name} {self.language_settings['and_word']} {self.first_obj.second_subject.name}" # type: ignore
|
|
951
|
-
|
|
952
|
-
# Top left section
|
|
953
|
-
# First subject
|
|
954
|
-
latitude = convert_latitude_coordinate_to_string(
|
|
955
|
-
self.first_obj.first_subject.lat, # type: ignore
|
|
956
|
-
self.language_settings["north_letter"],
|
|
957
|
-
self.language_settings["south_letter"],
|
|
958
|
-
)
|
|
959
|
-
longitude = convert_longitude_coordinate_to_string(
|
|
960
|
-
self.first_obj.first_subject.lng, # type: ignore
|
|
961
|
-
self.language_settings["east_letter"],
|
|
962
|
-
self.language_settings["west_letter"],
|
|
963
|
-
)
|
|
964
|
-
|
|
965
|
-
# Second subject
|
|
966
|
-
latitude_string = convert_latitude_coordinate_to_string(
|
|
967
|
-
self.first_obj.second_subject.lat, # type: ignore
|
|
968
|
-
self.language_settings["north_letter"],
|
|
969
|
-
self.language_settings["south_letter"],
|
|
970
|
-
)
|
|
971
|
-
longitude_string = convert_longitude_coordinate_to_string(
|
|
972
|
-
self.first_obj.second_subject.lng, # type: ignore
|
|
973
|
-
self.language_settings["east_letter"],
|
|
974
|
-
self.language_settings["west_letter"],
|
|
975
|
-
)
|
|
976
|
-
|
|
977
|
-
template_dict["top_left_0"] = f"{self.first_obj.first_subject.name}" # type: ignore
|
|
978
|
-
template_dict["top_left_1"] = f"{datetime.fromisoformat(self.first_obj.first_subject.iso_formatted_local_datetime).strftime('%Y-%m-%d %H:%M')}" # type: ignore
|
|
979
|
-
template_dict["top_left_2"] = f"{latitude} {longitude}"
|
|
980
|
-
template_dict["top_left_3"] = self.first_obj.second_subject.name # type: ignore
|
|
981
|
-
template_dict["top_left_4"] = f"{datetime.fromisoformat(self.first_obj.second_subject.iso_formatted_local_datetime).strftime('%Y-%m-%d %H:%M')}" # type: ignore
|
|
982
|
-
template_dict["top_left_5"] = f"{latitude_string} / {longitude_string}"
|
|
983
|
-
|
|
984
|
-
# Bottom left section
|
|
985
|
-
if self.first_obj.zodiac_type == "Tropic":
|
|
986
|
-
zodiac_info = f"{self.language_settings.get('zodiac', 'Zodiac')}: {self.language_settings.get('tropical', 'Tropical')}"
|
|
987
|
-
else:
|
|
988
|
-
mode_const = "SIDM_" + self.first_obj.sidereal_mode # type: ignore
|
|
989
|
-
mode_name = swe.get_ayanamsa_name(getattr(swe, mode_const))
|
|
990
|
-
zodiac_info = f"{self.language_settings.get('ayanamsa', 'Ayanamsa')}: {mode_name}"
|
|
991
|
-
|
|
992
|
-
template_dict["bottom_left_0"] = zodiac_info
|
|
993
|
-
template_dict["bottom_left_1"] = f"{self.language_settings.get('houses_system_' + self.first_obj.houses_system_identifier, self.first_obj.houses_system_name)} {self.language_settings.get('houses', 'Houses')}"
|
|
994
|
-
template_dict["bottom_left_2"] = f'{self.language_settings.get("perspective_type", "Perspective")}: {self.first_obj.first_subject.perspective_type}' # type: ignore
|
|
995
|
-
template_dict["bottom_left_3"] = f'{self.language_settings.get("composite_chart", "Composite Chart")} - {self.language_settings.get("midpoints", "Midpoints")}'
|
|
996
|
-
template_dict["bottom_left_4"] = ""
|
|
997
|
-
|
|
998
|
-
# Moon phase section calculations
|
|
999
|
-
if self.first_obj.lunar_phase is not None:
|
|
1000
|
-
template_dict["makeLunarPhase"] = makeLunarPhase(self.first_obj.lunar_phase["degrees_between_s_m"], self.geolat)
|
|
1001
|
-
else:
|
|
1002
|
-
template_dict["makeLunarPhase"] = ""
|
|
1003
|
-
|
|
1004
|
-
# Houses and planet drawing
|
|
1005
|
-
template_dict["makeMainHousesGrid"] = draw_main_house_grid(
|
|
1006
|
-
main_subject_houses_list=first_subject_houses_list,
|
|
1007
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1008
|
-
house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
1009
|
-
)
|
|
1010
|
-
template_dict["makeSecondaryHousesGrid"] = ""
|
|
1011
|
-
|
|
1012
|
-
template_dict["makeHouses"] = draw_houses_cusps_and_text_number(
|
|
1013
|
-
r=self.main_radius,
|
|
1014
|
-
first_subject_houses_list=first_subject_houses_list,
|
|
1015
|
-
standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
|
|
1016
|
-
first_house_color=self.planets_settings[12]["color"],
|
|
1017
|
-
tenth_house_color=self.planets_settings[13]["color"],
|
|
1018
|
-
seventh_house_color=self.planets_settings[14]["color"],
|
|
1019
|
-
fourth_house_color=self.planets_settings[15]["color"],
|
|
1020
|
-
c1=self.first_circle_radius,
|
|
1021
|
-
c3=self.third_circle_radius,
|
|
1022
|
-
chart_type=self.chart_type,
|
|
1023
|
-
)
|
|
1024
|
-
|
|
1025
|
-
template_dict["makePlanets"] = draw_planets(
|
|
1026
|
-
available_planets_setting=self.available_planets_setting,
|
|
1027
|
-
chart_type=self.chart_type,
|
|
1028
|
-
radius=self.main_radius,
|
|
1029
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1030
|
-
third_circle_radius=self.third_circle_radius,
|
|
1031
|
-
main_subject_first_house_degree_ut=self.first_obj.first_house.abs_pos,
|
|
1032
|
-
main_subject_seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
1033
|
-
)
|
|
1034
|
-
|
|
1035
|
-
subject_name = f"{self.first_obj.first_subject.name} {self.language_settings['and_word']} {self.first_obj.second_subject.name}" # type: ignore
|
|
1036
|
-
|
|
1037
|
-
template_dict["makeMainPlanetGrid"] = draw_main_planet_grid(
|
|
1038
|
-
planets_and_houses_grid_title=self.language_settings["planets_and_house"],
|
|
1039
|
-
subject_name=subject_name,
|
|
1040
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1041
|
-
chart_type=self.chart_type,
|
|
1042
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1043
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1044
|
-
)
|
|
1045
|
-
template_dict["makeSecondaryPlanetGrid"] = ""
|
|
1046
|
-
template_dict["makeHouseComparisonGrid"] = ""
|
|
1047
|
-
|
|
1048
|
-
elif self.chart_type == "Transit":
|
|
1049
|
-
|
|
1050
|
-
# Transit has no Element Percentages
|
|
1051
|
-
template_dict["elements_string"] = ""
|
|
1052
|
-
template_dict["fire_string"] = ""
|
|
1053
|
-
template_dict["earth_string"] = ""
|
|
1054
|
-
template_dict["air_string"] = ""
|
|
1055
|
-
template_dict["water_string"] = ""
|
|
1056
|
-
|
|
1057
|
-
# Transit has no Qualities Percentages
|
|
1058
|
-
template_dict["qualities_string"] = ""
|
|
1059
|
-
template_dict["cardinal_string"] = ""
|
|
1060
|
-
template_dict["fixed_string"] = ""
|
|
1061
|
-
template_dict["mutable_string"] = ""
|
|
1062
|
-
|
|
1063
|
-
# Set viewbox
|
|
1064
|
-
if self.double_chart_aspect_grid_type == "table":
|
|
1065
|
-
template_dict["viewbox"] = self._TRANSIT_CHART_WITH_TABLE_VIWBOX
|
|
1066
|
-
else:
|
|
1067
|
-
template_dict["viewbox"] = self._WIDE_CHART_VIEWBOX
|
|
1068
|
-
|
|
1069
|
-
# Get houses list for secondary subject
|
|
1070
|
-
second_subject_houses_list = get_houses_list(self.second_obj) # type: ignore
|
|
1071
|
-
|
|
1072
|
-
# Rings and circles
|
|
1073
|
-
template_dict["transitRing"] = draw_transit_ring(
|
|
1074
|
-
self.main_radius,
|
|
1075
|
-
self.chart_colors_settings["paper_1"],
|
|
1076
|
-
self.chart_colors_settings["zodiac_transit_ring_3"],
|
|
1077
|
-
)
|
|
1078
|
-
template_dict["degreeRing"] = draw_transit_ring_degree_steps(self.main_radius, self.first_obj.seventh_house.abs_pos)
|
|
1079
|
-
template_dict["background_circle"] = draw_background_circle(
|
|
1080
|
-
self.main_radius,
|
|
1081
|
-
self.chart_colors_settings["paper_1"],
|
|
1082
|
-
self.chart_colors_settings["paper_1"],
|
|
1083
|
-
)
|
|
1084
|
-
template_dict["first_circle"] = draw_first_circle(
|
|
1085
|
-
self.main_radius,
|
|
1086
|
-
self.chart_colors_settings["zodiac_transit_ring_2"],
|
|
1087
|
-
self.chart_type,
|
|
1088
|
-
)
|
|
1089
|
-
template_dict["second_circle"] = draw_second_circle(
|
|
1090
|
-
self.main_radius,
|
|
1091
|
-
self.chart_colors_settings["zodiac_transit_ring_1"],
|
|
1092
|
-
self.chart_colors_settings["paper_1"],
|
|
1093
|
-
self.chart_type,
|
|
1094
|
-
)
|
|
1095
|
-
template_dict["third_circle"] = draw_third_circle(
|
|
1096
|
-
self.main_radius,
|
|
1097
|
-
self.chart_colors_settings["zodiac_transit_ring_0"],
|
|
1098
|
-
self.chart_colors_settings["paper_1"],
|
|
1099
|
-
self.chart_type,
|
|
1100
|
-
self.third_circle_radius,
|
|
1101
|
-
)
|
|
1102
|
-
|
|
1103
|
-
# Aspects
|
|
1104
|
-
if self.double_chart_aspect_grid_type == "list":
|
|
1105
|
-
title = f'{self.first_obj.name} - {self.language_settings.get("transit_aspects", "Transit Aspects")}'
|
|
1106
|
-
template_dict["makeAspectGrid"] = ""
|
|
1107
|
-
template_dict["makeDoubleChartAspectList"] = draw_transit_aspect_list(title, self.aspects_list, self.planets_settings, self.aspects_settings)
|
|
1108
|
-
else:
|
|
1109
|
-
template_dict["makeAspectGrid"] = ""
|
|
1110
|
-
template_dict["makeDoubleChartAspectList"] = draw_transit_aspect_grid(
|
|
1111
|
-
self.chart_colors_settings["paper_0"],
|
|
1112
|
-
self.available_planets_setting,
|
|
1113
|
-
self.aspects_list,
|
|
1114
|
-
550,
|
|
1115
|
-
450,
|
|
1116
|
-
)
|
|
1117
|
-
|
|
1118
|
-
template_dict["makeAspects"] = self._draw_all_transit_aspects_lines(self.main_radius, self.main_radius - 160)
|
|
1119
|
-
|
|
1120
|
-
# Chart title
|
|
1121
|
-
template_dict["stringTitle"] = f"{self.language_settings['transits']} {format_datetime_with_timezone(self.second_obj.iso_formatted_local_datetime)}" # type: ignore
|
|
1122
|
-
|
|
1123
|
-
# Top left section
|
|
1124
|
-
latitude_string = convert_latitude_coordinate_to_string(self.geolat, self.language_settings["north"], self.language_settings["south"])
|
|
1125
|
-
longitude_string = convert_longitude_coordinate_to_string(self.geolon, self.language_settings["east"], self.language_settings["west"])
|
|
1126
|
-
|
|
1127
|
-
template_dict["top_left_0"] = template_dict["top_left_0"] = f'{self.first_obj.name}'
|
|
1128
|
-
template_dict["top_left_1"] = f"{format_location_string(self.first_obj.city)}, {self.first_obj.nation}" # type: ignore
|
|
1129
|
-
template_dict["top_left_2"] = format_datetime_with_timezone(self.first_obj.iso_formatted_local_datetime) # type: ignore
|
|
1130
|
-
template_dict["top_left_3"] = f"{self.language_settings['latitude']}: {latitude_string}"
|
|
1131
|
-
template_dict["top_left_4"] = f"{self.language_settings['longitude']}: {longitude_string}"
|
|
1132
|
-
template_dict["top_left_5"] = ""#f"{self.language_settings['type']}: {self.language_settings.get(self.chart_type, self.chart_type)}"
|
|
1133
|
-
|
|
1134
|
-
# Bottom left section
|
|
1135
|
-
if self.first_obj.zodiac_type == "Tropic":
|
|
1136
|
-
zodiac_info = f"{self.language_settings.get('zodiac', 'Zodiac')}: {self.language_settings.get('tropical', 'Tropical')}"
|
|
1137
|
-
else:
|
|
1138
|
-
mode_const = "SIDM_" + self.first_obj.sidereal_mode # type: ignore
|
|
1139
|
-
mode_name = swe.get_ayanamsa_name(getattr(swe, mode_const))
|
|
1140
|
-
zodiac_info = f"{self.language_settings.get('ayanamsa', 'Ayanamsa')}: {mode_name}"
|
|
1141
|
-
|
|
1142
|
-
template_dict["bottom_left_0"] = zodiac_info
|
|
1143
|
-
template_dict["bottom_left_1"] = f"{self.language_settings.get('domification', 'Domification')}: {self.language_settings.get('houses_system_' + self.first_obj.houses_system_identifier, self.first_obj.houses_system_name)}"
|
|
1144
|
-
|
|
1145
|
-
# Lunar phase information from second object (Transit) (optional)
|
|
1146
|
-
if self.second_obj is not None and hasattr(self.second_obj, 'lunar_phase') and self.second_obj.lunar_phase is not None:
|
|
1147
|
-
template_dict["bottom_left_2"] = f'{self.language_settings.get("lunation_day", "Lunation Day")}: {self.second_obj.lunar_phase.get("moon_phase", "")}' # type: ignore
|
|
1148
|
-
template_dict["bottom_left_3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get(self.second_obj.lunar_phase.moon_phase_name.lower().replace(" ", "_"), self.second_obj.lunar_phase.moon_phase_name)}'
|
|
1149
|
-
else:
|
|
1150
|
-
template_dict["bottom_left_2"] = ""
|
|
1151
|
-
template_dict["bottom_left_3"] = ""
|
|
1152
|
-
|
|
1153
|
-
template_dict["bottom_left_4"] = f'{self.language_settings.get("perspective_type", "Perspective")}: {self.language_settings.get(self.second_obj.perspective_type.lower().replace(" ", "_"), self.second_obj.perspective_type)}' # type: ignore
|
|
1154
|
-
|
|
1155
|
-
# Moon phase section calculations - use first_obj for visualization
|
|
1156
|
-
if self.first_obj.lunar_phase is not None:
|
|
1157
|
-
template_dict["makeLunarPhase"] = makeLunarPhase(self.first_obj.lunar_phase["degrees_between_s_m"], self.geolat)
|
|
1158
|
-
else:
|
|
1159
|
-
template_dict["makeLunarPhase"] = ""
|
|
1160
|
-
|
|
1161
|
-
# Houses and planet drawing
|
|
1162
|
-
template_dict["makeMainHousesGrid"] = draw_main_house_grid(
|
|
1163
|
-
main_subject_houses_list=first_subject_houses_list,
|
|
1164
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1165
|
-
house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
1166
|
-
)
|
|
1167
|
-
# template_dict["makeSecondaryHousesGrid"] = draw_secondary_house_grid(
|
|
1168
|
-
# secondary_subject_houses_list=second_subject_houses_list,
|
|
1169
|
-
# text_color=self.chart_colors_settings["paper_0"],
|
|
1170
|
-
# house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
1171
|
-
# )
|
|
1172
|
-
template_dict["makeSecondaryHousesGrid"] = ""
|
|
1173
|
-
|
|
1174
|
-
template_dict["makeHouses"] = draw_houses_cusps_and_text_number(
|
|
1175
|
-
r=self.main_radius,
|
|
1176
|
-
first_subject_houses_list=first_subject_houses_list,
|
|
1177
|
-
standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
|
|
1178
|
-
first_house_color=self.planets_settings[12]["color"],
|
|
1179
|
-
tenth_house_color=self.planets_settings[13]["color"],
|
|
1180
|
-
seventh_house_color=self.planets_settings[14]["color"],
|
|
1181
|
-
fourth_house_color=self.planets_settings[15]["color"],
|
|
1182
|
-
c1=self.first_circle_radius,
|
|
1183
|
-
c3=self.third_circle_radius,
|
|
1184
|
-
chart_type=self.chart_type,
|
|
1185
|
-
second_subject_houses_list=second_subject_houses_list,
|
|
1186
|
-
transit_house_cusp_color=self.chart_colors_settings["houses_transit_line"],
|
|
1187
|
-
)
|
|
1188
|
-
|
|
1189
|
-
template_dict["makePlanets"] = draw_planets(
|
|
1190
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1191
|
-
available_planets_setting=self.available_planets_setting,
|
|
1192
|
-
second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
|
|
1193
|
-
radius=self.main_radius,
|
|
1194
|
-
main_subject_first_house_degree_ut=self.first_obj.first_house.abs_pos,
|
|
1195
|
-
main_subject_seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
1196
|
-
chart_type=self.chart_type,
|
|
1197
|
-
third_circle_radius=self.third_circle_radius,
|
|
1198
|
-
)
|
|
1199
|
-
|
|
1200
|
-
# Planet grids
|
|
1201
|
-
first_return_grid_title = f"{self.first_obj.name} ({self.language_settings.get('inner_wheel', 'Inner Wheel')})"
|
|
1202
|
-
second_return_grid_title = f"{self.language_settings.get('Transit', 'Transit')} ({self.language_settings.get('outer_wheel', 'Outer Wheel')})"
|
|
1203
|
-
template_dict["makeMainPlanetGrid"] = draw_main_planet_grid(
|
|
1204
|
-
planets_and_houses_grid_title="",
|
|
1205
|
-
subject_name=first_return_grid_title,
|
|
1206
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1207
|
-
chart_type=self.chart_type,
|
|
1208
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1209
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1210
|
-
)
|
|
1211
|
-
|
|
1212
|
-
template_dict["makeSecondaryPlanetGrid"] = draw_secondary_planet_grid(
|
|
1213
|
-
planets_and_houses_grid_title="",
|
|
1214
|
-
second_subject_name=second_return_grid_title,
|
|
1215
|
-
second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
|
|
1216
|
-
chart_type=self.chart_type,
|
|
1217
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1218
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1219
|
-
)
|
|
1220
|
-
|
|
1221
|
-
# House comparison grid
|
|
1222
|
-
house_comparison_factory = HouseComparisonFactory(
|
|
1223
|
-
first_subject=self.first_obj,
|
|
1224
|
-
second_subject=self.second_obj,
|
|
1225
|
-
active_points=self.active_points,
|
|
1226
|
-
)
|
|
1227
|
-
house_comparison = house_comparison_factory.get_house_comparison()
|
|
1228
|
-
|
|
1229
|
-
template_dict["makeHouseComparisonGrid"] = draw_single_house_comparison_grid(
|
|
1230
|
-
house_comparison,
|
|
1231
|
-
celestial_point_language=self.language_settings.get("celestial_points", "Celestial Points"),
|
|
1232
|
-
active_points=self.active_points,
|
|
1233
|
-
points_owner_subject_number=2, # The second subject is the Transit
|
|
1234
|
-
house_position_comparison_label=self.language_settings.get("house_position_comparison", "House Position Comparison"),
|
|
1235
|
-
return_point_label=self.language_settings.get("transit_point", "Transit Point"),
|
|
1236
|
-
natal_house_label=self.language_settings.get("house_position", "Natal House"),
|
|
1237
|
-
x_position=930,
|
|
1238
|
-
)
|
|
1239
|
-
|
|
1240
|
-
elif self.chart_type == "Synastry":
|
|
1241
|
-
# Set viewbox
|
|
1242
|
-
template_dict["viewbox"] = self._WIDE_CHART_VIEWBOX
|
|
1243
|
-
|
|
1244
|
-
# Get houses list for secondary subject
|
|
1245
|
-
second_subject_houses_list = get_houses_list(self.second_obj) # type: ignore
|
|
1246
|
-
|
|
1247
|
-
# Rings and circles
|
|
1248
|
-
template_dict["transitRing"] = draw_transit_ring(
|
|
1249
|
-
self.main_radius,
|
|
1250
|
-
self.chart_colors_settings["paper_1"],
|
|
1251
|
-
self.chart_colors_settings["zodiac_transit_ring_3"],
|
|
1252
|
-
)
|
|
1253
|
-
template_dict["degreeRing"] = draw_transit_ring_degree_steps(self.main_radius, self.first_obj.seventh_house.abs_pos)
|
|
1254
|
-
template_dict["background_circle"] = draw_background_circle(
|
|
1255
|
-
self.main_radius,
|
|
1256
|
-
self.chart_colors_settings["paper_1"],
|
|
1257
|
-
self.chart_colors_settings["paper_1"],
|
|
1258
|
-
)
|
|
1259
|
-
template_dict["first_circle"] = draw_first_circle(
|
|
1260
|
-
self.main_radius,
|
|
1261
|
-
self.chart_colors_settings["zodiac_transit_ring_2"],
|
|
1262
|
-
self.chart_type,
|
|
1263
|
-
)
|
|
1264
|
-
template_dict["second_circle"] = draw_second_circle(
|
|
1265
|
-
self.main_radius,
|
|
1266
|
-
self.chart_colors_settings["zodiac_transit_ring_1"],
|
|
1267
|
-
self.chart_colors_settings["paper_1"],
|
|
1268
|
-
self.chart_type,
|
|
1269
|
-
)
|
|
1270
|
-
template_dict["third_circle"] = draw_third_circle(
|
|
1271
|
-
self.main_radius,
|
|
1272
|
-
self.chart_colors_settings["zodiac_transit_ring_0"],
|
|
1273
|
-
self.chart_colors_settings["paper_1"],
|
|
1274
|
-
self.chart_type,
|
|
1275
|
-
self.third_circle_radius,
|
|
1276
|
-
)
|
|
1277
|
-
|
|
1278
|
-
# Aspects
|
|
1279
|
-
if self.double_chart_aspect_grid_type == "list":
|
|
1280
|
-
template_dict["makeAspectGrid"] = ""
|
|
1281
|
-
template_dict["makeDoubleChartAspectList"] = draw_transit_aspect_list(
|
|
1282
|
-
f"{self.first_obj.name} - {self.second_obj.name} {self.language_settings.get('synastry_aspects', 'Synastry Aspects')}", # type: ignore
|
|
1283
|
-
self.aspects_list,
|
|
1284
|
-
self.planets_settings,
|
|
1285
|
-
self.aspects_settings
|
|
1286
|
-
)
|
|
1287
|
-
else:
|
|
1288
|
-
template_dict["makeAspectGrid"] = ""
|
|
1289
|
-
template_dict["makeDoubleChartAspectList"] = draw_transit_aspect_grid(
|
|
1290
|
-
self.chart_colors_settings["paper_0"],
|
|
1291
|
-
self.available_planets_setting,
|
|
1292
|
-
self.aspects_list,
|
|
1293
|
-
550,
|
|
1294
|
-
450,
|
|
1295
|
-
)
|
|
1296
|
-
|
|
1297
|
-
template_dict["makeAspects"] = self._draw_all_transit_aspects_lines(self.main_radius, self.main_radius - 160)
|
|
1298
|
-
|
|
1299
|
-
# Chart title
|
|
1300
|
-
template_dict["stringTitle"] = f"{self.first_obj.name} {self.language_settings['and_word']} {self.second_obj.name}" # type: ignore
|
|
1301
|
-
|
|
1302
|
-
# Top left section
|
|
1303
|
-
template_dict["top_left_0"] = f"{self.first_obj.name}:"
|
|
1304
|
-
template_dict["top_left_1"] = f"{self.first_obj.city}, {self.first_obj.nation}" # type: ignore
|
|
1305
|
-
template_dict["top_left_2"] = format_datetime_with_timezone(self.first_obj.iso_formatted_local_datetime) # type: ignore
|
|
1306
|
-
template_dict["top_left_3"] = f"{self.second_obj.name}: " # type: ignore
|
|
1307
|
-
template_dict["top_left_4"] = f"{self.second_obj.city}, {self.second_obj.nation}" # type: ignore
|
|
1308
|
-
template_dict["top_left_5"] = format_datetime_with_timezone(self.second_obj.iso_formatted_local_datetime) # type: ignore
|
|
1309
|
-
|
|
1310
|
-
# Bottom left section
|
|
1311
|
-
if self.first_obj.zodiac_type == "Tropic":
|
|
1312
|
-
zodiac_info = f"{self.language_settings.get('zodiac', 'Zodiac')}: {self.language_settings.get('tropical', 'Tropical')}"
|
|
1313
|
-
else:
|
|
1314
|
-
mode_const = "SIDM_" + self.first_obj.sidereal_mode # type: ignore
|
|
1315
|
-
mode_name = swe.get_ayanamsa_name(getattr(swe, mode_const))
|
|
1316
|
-
zodiac_info = f"{self.language_settings.get('ayanamsa', 'Ayanamsa')}: {mode_name}"
|
|
1317
|
-
|
|
1318
|
-
template_dict["bottom_left_0"] = ""
|
|
1319
|
-
# FIXME!
|
|
1320
|
-
template_dict["bottom_left_1"] = "" # f"Compatibility Score: {16}/44" # type: ignore
|
|
1321
|
-
template_dict["bottom_left_2"] = zodiac_info
|
|
1322
|
-
template_dict["bottom_left_3"] = f"{self.language_settings.get('houses_system_' + self.first_obj.houses_system_identifier, self.first_obj.houses_system_name)} {self.language_settings.get('houses', 'Houses')}"
|
|
1323
|
-
template_dict["bottom_left_4"] = f'{self.language_settings.get("perspective_type", "Perspective")}: {self.language_settings.get(self.first_obj.perspective_type.lower().replace(" ", "_"), self.first_obj.perspective_type)}'
|
|
1324
|
-
|
|
1325
|
-
# Moon phase section calculations
|
|
1326
|
-
template_dict["makeLunarPhase"] = ""
|
|
1327
|
-
|
|
1328
|
-
# Houses and planet drawing
|
|
1329
|
-
template_dict["makeMainHousesGrid"] = draw_main_house_grid(
|
|
1330
|
-
main_subject_houses_list=first_subject_houses_list,
|
|
1331
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1332
|
-
house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
1333
|
-
)
|
|
1334
|
-
|
|
1335
|
-
template_dict["makeSecondaryHousesGrid"] = draw_secondary_house_grid(
|
|
1336
|
-
secondary_subject_houses_list=second_subject_houses_list,
|
|
1337
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1338
|
-
house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
1339
|
-
)
|
|
1340
|
-
|
|
1341
|
-
template_dict["makeHouses"] = draw_houses_cusps_and_text_number(
|
|
1342
|
-
r=self.main_radius,
|
|
1343
|
-
first_subject_houses_list=first_subject_houses_list,
|
|
1344
|
-
standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
|
|
1345
|
-
first_house_color=self.planets_settings[12]["color"],
|
|
1346
|
-
tenth_house_color=self.planets_settings[13]["color"],
|
|
1347
|
-
seventh_house_color=self.planets_settings[14]["color"],
|
|
1348
|
-
fourth_house_color=self.planets_settings[15]["color"],
|
|
1349
|
-
c1=self.first_circle_radius,
|
|
1350
|
-
c3=self.third_circle_radius,
|
|
1351
|
-
chart_type=self.chart_type,
|
|
1352
|
-
second_subject_houses_list=second_subject_houses_list,
|
|
1353
|
-
transit_house_cusp_color=self.chart_colors_settings["houses_transit_line"],
|
|
1354
|
-
)
|
|
1355
|
-
|
|
1356
|
-
template_dict["makePlanets"] = draw_planets(
|
|
1357
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1358
|
-
available_planets_setting=self.available_planets_setting,
|
|
1359
|
-
second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
|
|
1360
|
-
radius=self.main_radius,
|
|
1361
|
-
main_subject_first_house_degree_ut=self.first_obj.first_house.abs_pos,
|
|
1362
|
-
main_subject_seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
1363
|
-
chart_type=self.chart_type,
|
|
1364
|
-
third_circle_radius=self.third_circle_radius,
|
|
1365
|
-
)
|
|
1366
|
-
|
|
1367
|
-
# Planet grid
|
|
1368
|
-
template_dict["makeMainPlanetGrid"] = draw_main_planet_grid(
|
|
1369
|
-
planets_and_houses_grid_title="",
|
|
1370
|
-
subject_name=f"{self.first_obj.name} ({self.language_settings.get('inner_wheel', 'Inner Wheel')})",
|
|
1371
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1372
|
-
chart_type=self.chart_type,
|
|
1373
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1374
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1375
|
-
)
|
|
1376
|
-
template_dict["makeSecondaryPlanetGrid"] = draw_secondary_planet_grid(
|
|
1377
|
-
planets_and_houses_grid_title="",
|
|
1378
|
-
second_subject_name= f"{self.second_obj.name} ({self.language_settings.get('outer_wheel', 'Outer Wheel')})", # type: ignore
|
|
1379
|
-
second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
|
|
1380
|
-
chart_type=self.chart_type,
|
|
1381
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1382
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1383
|
-
)
|
|
1384
|
-
template_dict["makeHouseComparisonGrid"] = ""
|
|
1385
|
-
|
|
1386
|
-
elif self.chart_type == "Return":
|
|
1387
|
-
# Set viewbox
|
|
1388
|
-
template_dict["viewbox"] = self._ULTRA_WIDE_CHART_VIEWBOX
|
|
1389
|
-
|
|
1390
|
-
# Get houses list for secondary subject
|
|
1391
|
-
second_subject_houses_list = get_houses_list(self.second_obj) # type: ignore
|
|
1392
|
-
|
|
1393
|
-
# Rings and circles
|
|
1394
|
-
template_dict["transitRing"] = draw_transit_ring(
|
|
1395
|
-
self.main_radius,
|
|
1396
|
-
self.chart_colors_settings["paper_1"],
|
|
1397
|
-
self.chart_colors_settings["zodiac_transit_ring_3"],
|
|
1398
|
-
)
|
|
1399
|
-
template_dict["degreeRing"] = draw_transit_ring_degree_steps(self.main_radius, self.first_obj.seventh_house.abs_pos)
|
|
1400
|
-
template_dict["background_circle"] = draw_background_circle(
|
|
1401
|
-
self.main_radius,
|
|
1402
|
-
self.chart_colors_settings["paper_1"],
|
|
1403
|
-
self.chart_colors_settings["paper_1"],
|
|
1404
|
-
)
|
|
1405
|
-
template_dict["first_circle"] = draw_first_circle(
|
|
1406
|
-
self.main_radius,
|
|
1407
|
-
self.chart_colors_settings["zodiac_transit_ring_2"],
|
|
1408
|
-
self.chart_type,
|
|
1409
|
-
)
|
|
1410
|
-
template_dict["second_circle"] = draw_second_circle(
|
|
1411
|
-
self.main_radius,
|
|
1412
|
-
self.chart_colors_settings["zodiac_transit_ring_1"],
|
|
1413
|
-
self.chart_colors_settings["paper_1"],
|
|
1414
|
-
self.chart_type,
|
|
1415
|
-
)
|
|
1416
|
-
template_dict["third_circle"] = draw_third_circle(
|
|
1417
|
-
self.main_radius,
|
|
1418
|
-
self.chart_colors_settings["zodiac_transit_ring_0"],
|
|
1419
|
-
self.chart_colors_settings["paper_1"],
|
|
1420
|
-
self.chart_type,
|
|
1421
|
-
self.third_circle_radius,
|
|
1422
|
-
)
|
|
1423
|
-
|
|
1424
|
-
# Aspects
|
|
1425
|
-
if self.double_chart_aspect_grid_type == "list":
|
|
1426
|
-
title = self.language_settings.get("return_aspects", "Natal to Return Aspects")
|
|
1427
|
-
template_dict["makeAspectGrid"] = ""
|
|
1428
|
-
template_dict["makeDoubleChartAspectList"] = draw_transit_aspect_list(title, self.aspects_list, self.planets_settings, self.aspects_settings, max_columns=7)
|
|
1429
|
-
else:
|
|
1430
|
-
template_dict["makeAspectGrid"] = ""
|
|
1431
|
-
template_dict["makeDoubleChartAspectList"] = draw_transit_aspect_grid(
|
|
1432
|
-
self.chart_colors_settings["paper_0"],
|
|
1433
|
-
self.available_planets_setting,
|
|
1434
|
-
self.aspects_list,
|
|
1435
|
-
550,
|
|
1436
|
-
450,
|
|
1437
|
-
)
|
|
1438
|
-
|
|
1439
|
-
template_dict["makeAspects"] = self._draw_all_transit_aspects_lines(self.main_radius, self.main_radius - 160)
|
|
1440
|
-
|
|
1441
|
-
# Chart title
|
|
1442
|
-
if self.second_obj is not None and hasattr(self.second_obj, 'return_type') and self.second_obj.return_type == "Solar":
|
|
1443
|
-
template_dict["stringTitle"] = f"{self.first_obj.name} - {self.language_settings.get('solar_return', 'Solar Return')}"
|
|
1444
|
-
else:
|
|
1445
|
-
template_dict["stringTitle"] = f"{self.first_obj.name} - {self.language_settings.get('lunar_return', 'Lunar Return')}"
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
# Top left section
|
|
1449
|
-
# Subject
|
|
1450
|
-
latitude_string = convert_latitude_coordinate_to_string(self.first_obj.lat, self.language_settings["north"], self.language_settings["south"]) # type: ignore
|
|
1451
|
-
longitude_string = convert_longitude_coordinate_to_string(self.first_obj.lng, self.language_settings["east"], self.language_settings["west"]) # type: ignore
|
|
1452
|
-
|
|
1453
|
-
# Return
|
|
1454
|
-
return_latitude_string = convert_latitude_coordinate_to_string(self.second_obj.lat, self.language_settings["north"], self.language_settings["south"]) # type: ignore
|
|
1455
|
-
return_longitude_string = convert_longitude_coordinate_to_string(self.second_obj.lng, self.language_settings["east"], self.language_settings["west"]) # type: ignore
|
|
1456
|
-
|
|
1457
|
-
if self.second_obj is not None and hasattr(self.second_obj, 'return_type') and self.second_obj.return_type == "Solar":
|
|
1458
|
-
template_dict["top_left_0"] = f"{self.language_settings.get('solar_return', 'Solar Return')}:"
|
|
1459
|
-
else:
|
|
1460
|
-
template_dict["top_left_0"] = f"{self.language_settings.get('lunar_return', 'Lunar Return')}:"
|
|
1461
|
-
template_dict["top_left_1"] = format_datetime_with_timezone(self.second_obj.iso_formatted_local_datetime) # type: ignore
|
|
1462
|
-
template_dict["top_left_2"] = f"{return_latitude_string} / {return_longitude_string}"
|
|
1463
|
-
template_dict["top_left_3"] = f"{self.first_obj.name}"
|
|
1464
|
-
template_dict["top_left_4"] = format_datetime_with_timezone(self.first_obj.iso_formatted_local_datetime) # type: ignore
|
|
1465
|
-
template_dict["top_left_5"] = f"{latitude_string} / {longitude_string}"
|
|
1466
|
-
|
|
1467
|
-
# Bottom left section
|
|
1468
|
-
if self.first_obj.zodiac_type == "Tropic":
|
|
1469
|
-
zodiac_info = f"{self.language_settings.get('zodiac', 'Zodiac')}: {self.language_settings.get('tropical', 'Tropical')}"
|
|
1470
|
-
else:
|
|
1471
|
-
mode_const = "SIDM_" + self.first_obj.sidereal_mode # type: ignore
|
|
1472
|
-
mode_name = swe.get_ayanamsa_name(getattr(swe, mode_const))
|
|
1473
|
-
zodiac_info = f"{self.language_settings.get('ayanamsa', 'Ayanamsa')}: {mode_name}"
|
|
1474
|
-
|
|
1475
|
-
template_dict["bottom_left_0"] = zodiac_info
|
|
1476
|
-
template_dict["bottom_left_1"] = f"{self.language_settings.get('domification', 'Domification')}: {self.language_settings.get('houses_system_' + self.first_obj.houses_system_identifier, self.first_obj.houses_system_name)}"
|
|
1477
|
-
|
|
1478
|
-
# Lunar phase information (optional)
|
|
1479
|
-
if self.first_obj.lunar_phase is not None:
|
|
1480
|
-
template_dict["bottom_left_2"] = f'{self.language_settings.get("lunation_day", "Lunation Day")}: {self.first_obj.lunar_phase.get("moon_phase", "")}'
|
|
1481
|
-
template_dict["bottom_left_3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get(self.first_obj.lunar_phase.moon_phase_name.lower().replace(" ", "_"), self.first_obj.lunar_phase.moon_phase_name)}'
|
|
1482
|
-
else:
|
|
1483
|
-
template_dict["bottom_left_2"] = ""
|
|
1484
|
-
template_dict["bottom_left_3"] = ""
|
|
1485
|
-
|
|
1486
|
-
template_dict["bottom_left_4"] = f'{self.language_settings.get("perspective_type", "Perspective")}: {self.language_settings.get(self.first_obj.perspective_type.lower().replace(" ", "_"), self.first_obj.perspective_type)}'
|
|
1487
|
-
|
|
1488
|
-
# Moon phase section calculations
|
|
1489
|
-
if self.first_obj.lunar_phase is not None:
|
|
1490
|
-
template_dict["makeLunarPhase"] = makeLunarPhase(self.first_obj.lunar_phase["degrees_between_s_m"], self.geolat)
|
|
1491
|
-
else:
|
|
1492
|
-
template_dict["makeLunarPhase"] = ""
|
|
1493
|
-
|
|
1494
|
-
# Houses and planet drawing
|
|
1495
|
-
template_dict["makeMainHousesGrid"] = draw_main_house_grid(
|
|
1496
|
-
main_subject_houses_list=first_subject_houses_list,
|
|
1497
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1498
|
-
house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
1499
|
-
)
|
|
1500
|
-
|
|
1501
|
-
template_dict["makeSecondaryHousesGrid"] = draw_secondary_house_grid(
|
|
1502
|
-
secondary_subject_houses_list=second_subject_houses_list,
|
|
1503
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1504
|
-
house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
1505
|
-
)
|
|
1506
|
-
|
|
1507
|
-
template_dict["makeHouses"] = draw_houses_cusps_and_text_number(
|
|
1508
|
-
r=self.main_radius,
|
|
1509
|
-
first_subject_houses_list=first_subject_houses_list,
|
|
1510
|
-
standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
|
|
1511
|
-
first_house_color=self.planets_settings[12]["color"],
|
|
1512
|
-
tenth_house_color=self.planets_settings[13]["color"],
|
|
1513
|
-
seventh_house_color=self.planets_settings[14]["color"],
|
|
1514
|
-
fourth_house_color=self.planets_settings[15]["color"],
|
|
1515
|
-
c1=self.first_circle_radius,
|
|
1516
|
-
c3=self.third_circle_radius,
|
|
1517
|
-
chart_type=self.chart_type,
|
|
1518
|
-
second_subject_houses_list=second_subject_houses_list,
|
|
1519
|
-
transit_house_cusp_color=self.chart_colors_settings["houses_transit_line"],
|
|
1520
|
-
)
|
|
1521
|
-
|
|
1522
|
-
template_dict["makePlanets"] = draw_planets(
|
|
1523
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1524
|
-
available_planets_setting=self.available_planets_setting,
|
|
1525
|
-
second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
|
|
1526
|
-
radius=self.main_radius,
|
|
1527
|
-
main_subject_first_house_degree_ut=self.first_obj.first_house.abs_pos,
|
|
1528
|
-
main_subject_seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
1529
|
-
chart_type=self.chart_type,
|
|
1530
|
-
third_circle_radius=self.third_circle_radius,
|
|
1531
|
-
)
|
|
1532
|
-
|
|
1533
|
-
# Planet grid
|
|
1534
|
-
if self.second_obj is not None and hasattr(self.second_obj, 'return_type') and self.second_obj.return_type == "Solar":
|
|
1535
|
-
first_return_grid_title = f"{self.first_obj.name} ({self.language_settings.get('inner_wheel', 'Inner Wheel')})"
|
|
1536
|
-
second_return_grid_title = f"{self.language_settings.get('solar_return', 'Solar Return')} ({self.language_settings.get('outer_wheel', 'Outer Wheel')})"
|
|
1537
|
-
else:
|
|
1538
|
-
first_return_grid_title = f"{self.first_obj.name} ({self.language_settings.get('inner_wheel', 'Inner Wheel')})"
|
|
1539
|
-
second_return_grid_title = f'{self.language_settings.get("lunar_return", "Lunar Return")} ({self.language_settings.get("outer_wheel", "Outer Wheel")})'
|
|
1540
|
-
template_dict["makeMainPlanetGrid"] = draw_main_planet_grid(
|
|
1541
|
-
planets_and_houses_grid_title="",
|
|
1542
|
-
subject_name=first_return_grid_title,
|
|
1543
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1544
|
-
chart_type=self.chart_type,
|
|
1545
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1546
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1547
|
-
)
|
|
1548
|
-
template_dict["makeSecondaryPlanetGrid"] = draw_secondary_planet_grid(
|
|
1549
|
-
planets_and_houses_grid_title="",
|
|
1550
|
-
second_subject_name=second_return_grid_title,
|
|
1551
|
-
second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
|
|
1552
|
-
chart_type=self.chart_type,
|
|
1553
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1554
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1555
|
-
)
|
|
1556
|
-
|
|
1557
|
-
house_comparison_factory = HouseComparisonFactory(
|
|
1558
|
-
first_subject=self.first_obj,
|
|
1559
|
-
second_subject=self.second_obj,
|
|
1560
|
-
active_points=self.active_points,
|
|
1561
|
-
)
|
|
1562
|
-
house_comparison = house_comparison_factory.get_house_comparison()
|
|
1563
|
-
|
|
1564
|
-
template_dict["makeHouseComparisonGrid"] = draw_house_comparison_grid(
|
|
1565
|
-
house_comparison,
|
|
1566
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1567
|
-
active_points=self.active_points,
|
|
1568
|
-
points_owner_subject_number=2, # The second subject is the Solar Return
|
|
1569
|
-
house_position_comparison_label=self.language_settings.get("house_position_comparison", "House Position Comparison"),
|
|
1570
|
-
return_point_label=self.language_settings.get("return_point", "Return Point"),
|
|
1571
|
-
return_label=self.language_settings.get("Return", "Return"),
|
|
1572
|
-
radix_label=self.language_settings.get("Natal", "Natal"),
|
|
1573
|
-
)
|
|
1574
|
-
|
|
1575
|
-
elif self.chart_type == "SingleWheelReturn":
|
|
1576
|
-
# Set viewbox
|
|
1577
|
-
template_dict["viewbox"] = self._BASIC_CHART_VIEWBOX
|
|
1578
|
-
|
|
1579
|
-
# Rings and circles
|
|
1580
|
-
template_dict["transitRing"] = ""
|
|
1581
|
-
template_dict["degreeRing"] = draw_degree_ring(
|
|
1582
|
-
self.main_radius,
|
|
1583
|
-
self.first_circle_radius,
|
|
1584
|
-
self.first_obj.seventh_house.abs_pos,
|
|
1585
|
-
self.chart_colors_settings["paper_0"],
|
|
1586
|
-
)
|
|
1587
|
-
template_dict["background_circle"] = draw_background_circle(
|
|
1588
|
-
self.main_radius,
|
|
1589
|
-
self.chart_colors_settings["paper_1"],
|
|
1590
|
-
self.chart_colors_settings["paper_1"],
|
|
1591
|
-
)
|
|
1592
|
-
template_dict["first_circle"] = draw_first_circle(
|
|
1593
|
-
self.main_radius,
|
|
1594
|
-
self.chart_colors_settings["zodiac_radix_ring_2"],
|
|
1595
|
-
self.chart_type,
|
|
1596
|
-
self.first_circle_radius,
|
|
1597
|
-
)
|
|
1598
|
-
template_dict["second_circle"] = draw_second_circle(
|
|
1599
|
-
self.main_radius,
|
|
1600
|
-
self.chart_colors_settings["zodiac_radix_ring_1"],
|
|
1601
|
-
self.chart_colors_settings["paper_1"],
|
|
1602
|
-
self.chart_type,
|
|
1603
|
-
self.second_circle_radius,
|
|
1604
|
-
)
|
|
1605
|
-
template_dict["third_circle"] = draw_third_circle(
|
|
1606
|
-
self.main_radius,
|
|
1607
|
-
self.chart_colors_settings["zodiac_radix_ring_0"],
|
|
1608
|
-
self.chart_colors_settings["paper_1"],
|
|
1609
|
-
self.chart_type,
|
|
1610
|
-
self.third_circle_radius,
|
|
1611
|
-
)
|
|
1612
|
-
|
|
1613
|
-
# Aspects
|
|
1614
|
-
template_dict["makeDoubleChartAspectList"] = ""
|
|
1615
|
-
template_dict["makeAspectGrid"] = draw_aspect_grid(
|
|
1616
|
-
self.chart_colors_settings["paper_0"],
|
|
1617
|
-
self.available_planets_setting,
|
|
1618
|
-
self.aspects_list,
|
|
1619
|
-
)
|
|
1620
|
-
template_dict["makeAspects"] = self._draw_all_aspects_lines(self.main_radius, self.main_radius - self.third_circle_radius)
|
|
1621
|
-
|
|
1622
|
-
# Chart title
|
|
1623
|
-
template_dict["stringTitle"] = self.first_obj.name
|
|
1624
|
-
|
|
1625
|
-
# Top left section
|
|
1626
|
-
latitude_string = convert_latitude_coordinate_to_string(self.geolat, self.language_settings["north"], self.language_settings["south"])
|
|
1627
|
-
longitude_string = convert_longitude_coordinate_to_string(self.geolon, self.language_settings["east"], self.language_settings["west"])
|
|
1628
|
-
|
|
1629
|
-
template_dict["top_left_0"] = f'{self.language_settings["info"]}:'
|
|
1630
|
-
template_dict["top_left_1"] = format_datetime_with_timezone(self.first_obj.iso_formatted_local_datetime) # type: ignore
|
|
1631
|
-
template_dict["top_left_2"] = f"{self.first_obj.city}, {self.first_obj.nation}"
|
|
1632
|
-
template_dict["top_left_3"] = f"{self.language_settings['latitude']}: {latitude_string}"
|
|
1633
|
-
template_dict["top_left_4"] = f"{self.language_settings['longitude']}: {longitude_string}"
|
|
1634
|
-
|
|
1635
|
-
if hasattr(self.first_obj, 'return_type') and self.first_obj.return_type == "Solar":
|
|
1636
|
-
template_dict["top_left_5"] = f"{self.language_settings['type']}: {self.language_settings.get('solar_return', 'Solar Return')}"
|
|
1637
|
-
else:
|
|
1638
|
-
template_dict["top_left_5"] = f"{self.language_settings['type']}: {self.language_settings.get('lunar_return', 'Lunar Return')}"
|
|
1639
|
-
|
|
1640
|
-
# Bottom left section
|
|
1641
|
-
if self.first_obj.zodiac_type == "Tropic":
|
|
1642
|
-
zodiac_info = f"{self.language_settings.get('zodiac', 'Zodiac')}: {self.language_settings.get('tropical', 'Tropical')}"
|
|
1643
|
-
else:
|
|
1644
|
-
mode_const = "SIDM_" + self.first_obj.sidereal_mode # type: ignore
|
|
1645
|
-
mode_name = swe.get_ayanamsa_name(getattr(swe, mode_const))
|
|
1646
|
-
zodiac_info = f"{self.language_settings.get('ayanamsa', 'Ayanamsa')}: {mode_name}"
|
|
1647
|
-
|
|
1648
|
-
template_dict["bottom_left_0"] = zodiac_info
|
|
1649
|
-
template_dict["bottom_left_1"] = f"{self.language_settings.get('houses_system_' + self.first_obj.houses_system_identifier, self.first_obj.houses_system_name)} {self.language_settings.get('houses', 'Houses')}"
|
|
1650
|
-
|
|
1651
|
-
# Lunar phase information (optional)
|
|
1652
|
-
if self.first_obj.lunar_phase is not None:
|
|
1653
|
-
template_dict["bottom_left_2"] = f'{self.language_settings.get("lunation_day", "Lunation Day")}: {self.first_obj.lunar_phase.get("moon_phase", "")}'
|
|
1654
|
-
template_dict["bottom_left_3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get(self.first_obj.lunar_phase.moon_phase_name.lower().replace(" ", "_"), self.first_obj.lunar_phase.moon_phase_name)}'
|
|
1655
|
-
else:
|
|
1656
|
-
template_dict["bottom_left_2"] = ""
|
|
1657
|
-
template_dict["bottom_left_3"] = ""
|
|
1658
|
-
|
|
1659
|
-
template_dict["bottom_left_4"] = f'{self.language_settings.get("perspective_type", "Perspective")}: {self.language_settings.get(self.first_obj.perspective_type.lower().replace(" ", "_"), self.first_obj.perspective_type)}'
|
|
1660
|
-
|
|
1661
|
-
# Moon phase section calculations
|
|
1662
|
-
if self.first_obj.lunar_phase is not None:
|
|
1663
|
-
template_dict["makeLunarPhase"] = makeLunarPhase(self.first_obj.lunar_phase["degrees_between_s_m"], self.geolat)
|
|
1664
|
-
else:
|
|
1665
|
-
template_dict["makeLunarPhase"] = ""
|
|
1666
|
-
|
|
1667
|
-
# Houses and planet drawing
|
|
1668
|
-
template_dict["makeMainHousesGrid"] = draw_main_house_grid(
|
|
1669
|
-
main_subject_houses_list=first_subject_houses_list,
|
|
1670
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1671
|
-
house_cusp_generale_name_label=self.language_settings["cusp"],
|
|
1672
|
-
)
|
|
1673
|
-
template_dict["makeSecondaryHousesGrid"] = ""
|
|
1674
|
-
|
|
1675
|
-
template_dict["makeHouses"] = draw_houses_cusps_and_text_number(
|
|
1676
|
-
r=self.main_radius,
|
|
1677
|
-
first_subject_houses_list=first_subject_houses_list,
|
|
1678
|
-
standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
|
|
1679
|
-
first_house_color=self.planets_settings[12]["color"],
|
|
1680
|
-
tenth_house_color=self.planets_settings[13]["color"],
|
|
1681
|
-
seventh_house_color=self.planets_settings[14]["color"],
|
|
1682
|
-
fourth_house_color=self.planets_settings[15]["color"],
|
|
1683
|
-
c1=self.first_circle_radius,
|
|
1684
|
-
c3=self.third_circle_radius,
|
|
1685
|
-
chart_type=self.chart_type,
|
|
1686
|
-
)
|
|
1687
|
-
|
|
1688
|
-
template_dict["makePlanets"] = draw_planets(
|
|
1689
|
-
available_planets_setting=self.available_planets_setting,
|
|
1690
|
-
chart_type=self.chart_type,
|
|
1691
|
-
radius=self.main_radius,
|
|
1692
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1693
|
-
third_circle_radius=self.third_circle_radius,
|
|
1694
|
-
main_subject_first_house_degree_ut=self.first_obj.first_house.abs_pos,
|
|
1695
|
-
main_subject_seventh_house_degree_ut=self.first_obj.seventh_house.abs_pos,
|
|
1696
|
-
)
|
|
1697
|
-
|
|
1698
|
-
template_dict["makeMainPlanetGrid"] = draw_main_planet_grid(
|
|
1699
|
-
planets_and_houses_grid_title=self.language_settings["planets_and_house"],
|
|
1700
|
-
subject_name=self.first_obj.name,
|
|
1701
|
-
available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
|
|
1702
|
-
chart_type=self.chart_type,
|
|
1703
|
-
text_color=self.chart_colors_settings["paper_0"],
|
|
1704
|
-
celestial_point_language=self.language_settings["celestial_points"],
|
|
1705
|
-
)
|
|
1706
|
-
template_dict["makeSecondaryPlanetGrid"] = ""
|
|
1707
|
-
template_dict["makeHouseComparisonGrid"] = ""
|
|
1708
|
-
|
|
1709
|
-
return ChartTemplateDictionary(**template_dict)
|
|
1710
|
-
|
|
1711
|
-
def makeTemplate(self, minify: bool = False, remove_css_variables=False) -> str:
|
|
1712
|
-
"""
|
|
1713
|
-
Render the full chart SVG as a string.
|
|
1714
|
-
|
|
1715
|
-
Reads the XML template, substitutes variables, and optionally inlines CSS
|
|
1716
|
-
variables and minifies the output.
|
|
1717
|
-
|
|
1718
|
-
Args:
|
|
1719
|
-
minify (bool): Remove whitespace and quotes for compactness.
|
|
1720
|
-
remove_css_variables (bool): Embed CSS variable definitions.
|
|
1721
|
-
|
|
1722
|
-
Returns:
|
|
1723
|
-
str: SVG markup as a string.
|
|
1724
|
-
"""
|
|
1725
|
-
td = self._create_template_dictionary()
|
|
1726
|
-
|
|
1727
|
-
DATA_DIR = Path(__file__).parent
|
|
1728
|
-
xml_svg = DATA_DIR / "templates" / "chart.xml"
|
|
1729
|
-
|
|
1730
|
-
# read template
|
|
1731
|
-
with open(xml_svg, "r", encoding="utf-8", errors="ignore") as f:
|
|
1732
|
-
template = Template(f.read()).substitute(td)
|
|
1733
|
-
|
|
1734
|
-
# return filename
|
|
1735
|
-
|
|
1736
|
-
logging.debug(f"Template dictionary keys: {td.keys()}")
|
|
1737
|
-
|
|
1738
|
-
self._create_template_dictionary()
|
|
1739
|
-
|
|
1740
|
-
if remove_css_variables:
|
|
1741
|
-
template = inline_css_variables_in_svg(template)
|
|
1742
|
-
|
|
1743
|
-
if minify:
|
|
1744
|
-
template = scourString(template).replace('"', "'").replace("\n", "").replace("\t", "").replace(" ", "").replace(" ", "")
|
|
1745
|
-
|
|
1746
|
-
else:
|
|
1747
|
-
template = template.replace('"', "'")
|
|
1748
|
-
|
|
1749
|
-
return template
|
|
1750
|
-
|
|
1751
|
-
def makeSVG(self, minify: bool = False, remove_css_variables=False):
|
|
1752
|
-
"""
|
|
1753
|
-
Generate and save the full chart SVG to disk.
|
|
1754
|
-
|
|
1755
|
-
Calls makeTemplate to render the SVG, then writes a file named
|
|
1756
|
-
"{subject.name} - {chart_type} Chart.svg" in the output directory.
|
|
1757
|
-
|
|
1758
|
-
Args:
|
|
1759
|
-
minify (bool): Pass-through to makeTemplate for compact output.
|
|
1760
|
-
remove_css_variables (bool): Pass-through to makeTemplate to embed CSS variables.
|
|
1761
|
-
|
|
1762
|
-
Returns:
|
|
1763
|
-
None
|
|
1764
|
-
"""
|
|
1765
|
-
|
|
1766
|
-
self.template = self.makeTemplate(minify, remove_css_variables)
|
|
1767
|
-
|
|
1768
|
-
if self.chart_type == "Return" and self.second_obj is not None and hasattr(self.second_obj, 'return_type') and self.second_obj.return_type == "Lunar":
|
|
1769
|
-
chartname = self.output_directory / f"{self.first_obj.name} - {self.chart_type} Chart - Lunar Return.svg"
|
|
1770
|
-
elif self.chart_type == "Return" and self.second_obj is not None and hasattr(self.second_obj, 'return_type') and self.second_obj.return_type == "Solar":
|
|
1771
|
-
chartname = self.output_directory / f"{self.first_obj.name} - {self.chart_type} Chart - Solar Return.svg"
|
|
1772
|
-
else:
|
|
1773
|
-
chartname = self.output_directory / f"{self.first_obj.name} - {self.chart_type} Chart.svg"
|
|
1774
|
-
|
|
1775
|
-
with open(chartname, "w", encoding="utf-8", errors="ignore") as output_file:
|
|
1776
|
-
output_file.write(self.template)
|
|
1777
|
-
|
|
1778
|
-
print(f"SVG Generated Correctly in: {chartname}")
|
|
1779
|
-
|
|
1780
|
-
def makeWheelOnlyTemplate(self, minify: bool = False, remove_css_variables=False):
|
|
1781
|
-
"""
|
|
1782
|
-
Render the wheel-only chart SVG as a string.
|
|
1783
|
-
|
|
1784
|
-
Reads the wheel-only XML template, substitutes chart data, and applies optional
|
|
1785
|
-
CSS inlining and minification.
|
|
1786
|
-
|
|
1787
|
-
Args:
|
|
1788
|
-
minify (bool): Remove whitespace and quotes for compactness.
|
|
1789
|
-
remove_css_variables (bool): Embed CSS variable definitions.
|
|
1790
|
-
|
|
1791
|
-
Returns:
|
|
1792
|
-
str: SVG markup for the chart wheel only.
|
|
1793
|
-
"""
|
|
1794
|
-
|
|
1795
|
-
with open(
|
|
1796
|
-
Path(__file__).parent / "templates" / "wheel_only.xml",
|
|
1797
|
-
"r",
|
|
1798
|
-
encoding="utf-8",
|
|
1799
|
-
errors="ignore",
|
|
1800
|
-
) as f:
|
|
1801
|
-
template = f.read()
|
|
1802
|
-
|
|
1803
|
-
template_dict = self._create_template_dictionary()
|
|
1804
|
-
template = Template(template).substitute(template_dict)
|
|
1805
|
-
|
|
1806
|
-
if remove_css_variables:
|
|
1807
|
-
template = inline_css_variables_in_svg(template)
|
|
1808
|
-
|
|
1809
|
-
if minify:
|
|
1810
|
-
template = scourString(template).replace('"', "'").replace("\n", "").replace("\t", "").replace(" ", "").replace(" ", "")
|
|
1811
|
-
|
|
1812
|
-
else:
|
|
1813
|
-
template = template.replace('"', "'")
|
|
1814
|
-
|
|
1815
|
-
return template
|
|
1816
|
-
|
|
1817
|
-
def makeWheelOnlySVG(self, minify: bool = False, remove_css_variables=False):
|
|
1818
|
-
"""
|
|
1819
|
-
Generate and save wheel-only chart SVG to disk.
|
|
1820
|
-
|
|
1821
|
-
Calls makeWheelOnlyTemplate and writes a file named
|
|
1822
|
-
"{subject.name} - {chart_type} Chart - Wheel Only.svg" in the output directory.
|
|
1823
|
-
|
|
1824
|
-
Args:
|
|
1825
|
-
minify (bool): Pass-through to makeWheelOnlyTemplate for compact output.
|
|
1826
|
-
remove_css_variables (bool): Pass-through to makeWheelOnlyTemplate to embed CSS variables.
|
|
1827
|
-
|
|
1828
|
-
Returns:
|
|
1829
|
-
None
|
|
1830
|
-
"""
|
|
1831
|
-
|
|
1832
|
-
template = self.makeWheelOnlyTemplate(minify, remove_css_variables)
|
|
1833
|
-
chartname = self.output_directory / f"{self.first_obj.name} - {self.chart_type} Chart - Wheel Only.svg"
|
|
1834
|
-
|
|
1835
|
-
with open(chartname, "w", encoding="utf-8", errors="ignore") as output_file:
|
|
1836
|
-
output_file.write(template)
|
|
1837
|
-
|
|
1838
|
-
print(f"SVG Generated Correctly in: {chartname}")
|
|
1839
|
-
|
|
1840
|
-
def makeAspectGridOnlyTemplate(self, minify: bool = False, remove_css_variables=False):
|
|
1841
|
-
"""
|
|
1842
|
-
Render the aspect-grid-only chart SVG as a string.
|
|
1843
|
-
|
|
1844
|
-
Reads the aspect-grid XML template, generates the aspect grid based on chart type,
|
|
1845
|
-
and applies optional CSS inlining and minification.
|
|
1846
|
-
|
|
1847
|
-
Args:
|
|
1848
|
-
minify (bool): Remove whitespace and quotes for compactness.
|
|
1849
|
-
remove_css_variables (bool): Embed CSS variable definitions.
|
|
1850
|
-
|
|
1851
|
-
Returns:
|
|
1852
|
-
str: SVG markup for the aspect grid only.
|
|
1853
|
-
"""
|
|
1854
|
-
|
|
1855
|
-
with open(
|
|
1856
|
-
Path(__file__).parent / "templates" / "aspect_grid_only.xml",
|
|
1857
|
-
"r",
|
|
1858
|
-
encoding="utf-8",
|
|
1859
|
-
errors="ignore",
|
|
1860
|
-
) as f:
|
|
1861
|
-
template = f.read()
|
|
1862
|
-
|
|
1863
|
-
template_dict = self._create_template_dictionary()
|
|
1864
|
-
|
|
1865
|
-
if self.chart_type in ["Transit", "Synastry", "Return"]:
|
|
1866
|
-
aspects_grid = draw_transit_aspect_grid(
|
|
1867
|
-
self.chart_colors_settings["paper_0"],
|
|
1868
|
-
self.available_planets_setting,
|
|
1869
|
-
self.aspects_list,
|
|
1870
|
-
)
|
|
1871
|
-
else:
|
|
1872
|
-
aspects_grid = draw_aspect_grid(
|
|
1873
|
-
self.chart_colors_settings["paper_0"],
|
|
1874
|
-
self.available_planets_setting,
|
|
1875
|
-
self.aspects_list,
|
|
1876
|
-
x_start=50,
|
|
1877
|
-
y_start=250,
|
|
1878
|
-
)
|
|
1879
|
-
|
|
1880
|
-
template = Template(template).substitute({**template_dict, "makeAspectGrid": aspects_grid})
|
|
1881
|
-
|
|
1882
|
-
if remove_css_variables:
|
|
1883
|
-
template = inline_css_variables_in_svg(template)
|
|
1884
|
-
|
|
1885
|
-
if minify:
|
|
1886
|
-
template = scourString(template).replace('"', "'").replace("\n", "").replace("\t", "").replace(" ", "").replace(" ", "")
|
|
1887
|
-
|
|
1888
|
-
else:
|
|
1889
|
-
template = template.replace('"', "'")
|
|
1890
|
-
|
|
1891
|
-
return template
|
|
1892
|
-
|
|
1893
|
-
def makeAspectGridOnlySVG(self, minify: bool = False, remove_css_variables=False):
|
|
1894
|
-
"""
|
|
1895
|
-
Generate and save aspect-grid-only chart SVG to disk.
|
|
1896
|
-
|
|
1897
|
-
Calls makeAspectGridOnlyTemplate and writes a file named
|
|
1898
|
-
"{subject.name} - {chart_type} Chart - Aspect Grid Only.svg" in the output directory.
|
|
1899
|
-
|
|
1900
|
-
Args:
|
|
1901
|
-
minify (bool): Pass-through to makeAspectGridOnlyTemplate for compact output.
|
|
1902
|
-
remove_css_variables (bool): Pass-through to makeAspectGridOnlyTemplate to embed CSS variables.
|
|
1903
|
-
|
|
1904
|
-
Returns:
|
|
1905
|
-
None
|
|
1906
|
-
"""
|
|
1907
|
-
|
|
1908
|
-
template = self.makeAspectGridOnlyTemplate(minify, remove_css_variables)
|
|
1909
|
-
chartname = self.output_directory / f"{self.first_obj.name} - {self.chart_type} Chart - Aspect Grid Only.svg"
|
|
1910
|
-
|
|
1911
|
-
with open(chartname, "w", encoding="utf-8", errors="ignore") as output_file:
|
|
1912
|
-
output_file.write(template)
|
|
1913
|
-
|
|
1914
|
-
print(f"SVG Generated Correctly in: {chartname}")
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
if __name__ == "__main__":
|
|
1918
|
-
from kerykeion.utilities import setup_logging
|
|
1919
|
-
from kerykeion.planetary_return_factory import PlanetaryReturnFactory
|
|
1920
|
-
from kerykeion.astrological_subject_factory import AstrologicalSubjectFactory
|
|
1921
|
-
|
|
1922
|
-
ACTIVE_PLANETS: list[AstrologicalPoint] = [
|
|
1923
|
-
"Sun", "Moon", "Pars_Fortunae", "Mercury", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", "Chiron", "True_Node"
|
|
1924
|
-
]
|
|
1925
|
-
|
|
1926
|
-
setup_logging(level="info")
|
|
1927
|
-
|
|
1928
|
-
subject = AstrologicalSubjectFactory.from_birth_data("John Lennon", 1940, 10, 9, 18, 30, "Liverpool", "GB")
|
|
1929
|
-
|
|
1930
|
-
return_factory = PlanetaryReturnFactory(
|
|
1931
|
-
subject,
|
|
1932
|
-
city="Los Angeles",
|
|
1933
|
-
nation="US",
|
|
1934
|
-
lng=-118.2437,
|
|
1935
|
-
lat=34.0522,
|
|
1936
|
-
tz_str="America/Los_Angeles",
|
|
1937
|
-
altitude=0
|
|
1938
|
-
)
|
|
1939
|
-
|
|
1940
|
-
###
|
|
1941
|
-
## Birth Chart
|
|
1942
|
-
birth_chart = KerykeionChartSVG(
|
|
1943
|
-
first_obj=subject,
|
|
1944
|
-
chart_language="IT",
|
|
1945
|
-
theme="strawberry",
|
|
1946
|
-
active_points=ACTIVE_PLANETS,
|
|
1947
|
-
)
|
|
1948
|
-
birth_chart.makeSVG() # minify=True, remove_css_variables=True)
|
|
1949
|
-
|
|
1950
|
-
###
|
|
1951
|
-
## Solar Return Chart
|
|
1952
|
-
solar_return = return_factory.next_return_from_iso_formatted_time(
|
|
1953
|
-
"2025-01-09T18:30:00+01:00", # UTC+1
|
|
1954
|
-
return_type="Solar",
|
|
1955
|
-
)
|
|
1956
|
-
solar_return_chart = KerykeionChartSVG(
|
|
1957
|
-
first_obj=subject, chart_type="Return",
|
|
1958
|
-
second_obj=solar_return,
|
|
1959
|
-
chart_language="IT",
|
|
1960
|
-
theme="classic",
|
|
1961
|
-
active_points=ACTIVE_PLANETS,
|
|
1962
|
-
)
|
|
1963
|
-
|
|
1964
|
-
solar_return_chart.makeSVG() # minify=True, remove_css_variables=True)
|
|
1965
|
-
|
|
1966
|
-
###
|
|
1967
|
-
## Single wheel return
|
|
1968
|
-
single_wheel_return_chart = KerykeionChartSVG(
|
|
1969
|
-
first_obj=solar_return,
|
|
1970
|
-
chart_type="SingleWheelReturn",
|
|
1971
|
-
second_obj=solar_return,
|
|
1972
|
-
chart_language="IT",
|
|
1973
|
-
theme="dark",
|
|
1974
|
-
active_points=ACTIVE_PLANETS,
|
|
1975
|
-
)
|
|
1976
|
-
|
|
1977
|
-
single_wheel_return_chart.makeSVG() # minify=True, remove_css_variables=True)
|
|
1978
|
-
|
|
1979
|
-
###
|
|
1980
|
-
## Lunar return
|
|
1981
|
-
lunar_return = return_factory.next_return_from_iso_formatted_time(
|
|
1982
|
-
"2025-01-09T18:30:00+01:00", # UTC+1
|
|
1983
|
-
return_type="Lunar",
|
|
1984
|
-
)
|
|
1985
|
-
lunar_return_chart = KerykeionChartSVG(
|
|
1986
|
-
first_obj=subject,
|
|
1987
|
-
chart_type="Return",
|
|
1988
|
-
second_obj=lunar_return,
|
|
1989
|
-
chart_language="IT",
|
|
1990
|
-
theme="dark",
|
|
1991
|
-
active_points=ACTIVE_PLANETS,
|
|
1992
|
-
)
|
|
1993
|
-
lunar_return_chart.makeSVG() # minify=True, remove_css_variables=True)
|
|
1994
|
-
|
|
1995
|
-
###
|
|
1996
|
-
## Transit Chart
|
|
1997
|
-
transit = AstrologicalSubjectFactory.from_iso_utc_time(
|
|
1998
|
-
"Transit",
|
|
1999
|
-
"2021-10-04T18:30:00+01:00",
|
|
2000
|
-
)
|
|
2001
|
-
transit_chart = KerykeionChartSVG(
|
|
2002
|
-
first_obj=subject,
|
|
2003
|
-
chart_type="Transit",
|
|
2004
|
-
second_obj=transit,
|
|
2005
|
-
chart_language="IT",
|
|
2006
|
-
theme="dark",
|
|
2007
|
-
active_points=ACTIVE_PLANETS
|
|
2008
|
-
)
|
|
2009
|
-
transit_chart.makeSVG() # minify=True, remove_css_variables=True)
|
|
2010
|
-
|
|
2011
|
-
###
|
|
2012
|
-
## Synastry Chart
|
|
2013
|
-
second_subject = AstrologicalSubjectFactory.from_birth_data("Yoko Ono", 1933, 2, 18, 18, 30, "Tokyo", "JP")
|
|
2014
|
-
synastry_chart = KerykeionChartSVG(
|
|
2015
|
-
first_obj=subject,
|
|
2016
|
-
chart_type="Synastry",
|
|
2017
|
-
second_obj=second_subject,
|
|
2018
|
-
chart_language="IT",
|
|
2019
|
-
theme="dark",
|
|
2020
|
-
active_points=ACTIVE_PLANETS
|
|
2021
|
-
)
|
|
2022
|
-
synastry_chart.makeSVG() # minify=True, remove_css_variables=True)
|
|
2023
|
-
|
|
2024
|
-
##
|
|
2025
|
-
# Transit Chart with Grid
|
|
2026
|
-
subject.name = "Grid"
|
|
2027
|
-
transit_chart_with_grid = KerykeionChartSVG(
|
|
2028
|
-
first_obj=subject,
|
|
2029
|
-
chart_type="Transit",
|
|
2030
|
-
second_obj=transit,
|
|
2031
|
-
chart_language="IT",
|
|
2032
|
-
theme="dark",
|
|
2033
|
-
active_points=ACTIVE_PLANETS,
|
|
2034
|
-
double_chart_aspect_grid_type="table"
|
|
2035
|
-
)
|
|
2036
|
-
transit_chart_with_grid.makeSVG() # minify=True, remove_css_variables=True)
|
|
2037
|
-
transit_chart_with_grid.makeAspectGridOnlySVG()
|
|
2038
|
-
transit_chart_with_grid.makeWheelOnlySVG()
|