kerykeion 4.12.3__py3-none-any.whl → 4.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kerykeion might be problematic. Click here for more details.

Files changed (36) hide show
  1. kerykeion/__init__.py +3 -1
  2. kerykeion/aspects/aspects_utils.py +40 -123
  3. kerykeion/aspects/natal_aspects.py +34 -25
  4. kerykeion/aspects/synastry_aspects.py +34 -28
  5. kerykeion/astrological_subject.py +199 -196
  6. kerykeion/charts/charts_utils.py +701 -62
  7. kerykeion/charts/draw_planets.py +407 -0
  8. kerykeion/charts/kerykeion_chart_svg.py +534 -1140
  9. kerykeion/charts/templates/aspect_grid_only.xml +452 -0
  10. kerykeion/charts/templates/chart.xml +88 -70
  11. kerykeion/charts/templates/wheel_only.xml +499 -0
  12. kerykeion/charts/themes/classic.css +82 -0
  13. kerykeion/charts/themes/dark-high-contrast.css +121 -0
  14. kerykeion/charts/themes/dark.css +121 -0
  15. kerykeion/charts/themes/light.css +117 -0
  16. kerykeion/enums.py +1 -0
  17. kerykeion/ephemeris_data.py +178 -0
  18. kerykeion/fetch_geonames.py +2 -3
  19. kerykeion/kr_types/chart_types.py +6 -16
  20. kerykeion/kr_types/kr_literals.py +12 -3
  21. kerykeion/kr_types/kr_models.py +77 -32
  22. kerykeion/kr_types/settings_models.py +4 -10
  23. kerykeion/relationship_score/__init__.py +2 -0
  24. kerykeion/relationship_score/relationship_score.py +175 -0
  25. kerykeion/relationship_score/relationship_score_factory.py +275 -0
  26. kerykeion/report.py +6 -3
  27. kerykeion/settings/kerykeion_settings.py +6 -1
  28. kerykeion/settings/kr.config.json +256 -102
  29. kerykeion/utilities.py +122 -217
  30. {kerykeion-4.12.3.dist-info → kerykeion-4.18.0.dist-info}/METADATA +40 -10
  31. kerykeion-4.18.0.dist-info/RECORD +42 -0
  32. kerykeion/relationship_score.py +0 -205
  33. kerykeion-4.12.3.dist-info/RECORD +0 -32
  34. {kerykeion-4.12.3.dist-info → kerykeion-4.18.0.dist-info}/LICENSE +0 -0
  35. {kerykeion-4.12.3.dist-info → kerykeion-4.18.0.dist-info}/WHEEL +0 -0
  36. {kerykeion-4.12.3.dist-info → kerykeion-4.18.0.dist-info}/entry_points.txt +0 -0
@@ -5,38 +5,46 @@
5
5
 
6
6
 
7
7
  import logging
8
+ from typing import get_args
8
9
 
9
10
  from kerykeion.settings.kerykeion_settings import get_settings
10
11
  from kerykeion.aspects.synastry_aspects import SynastryAspects
11
12
  from kerykeion.aspects.natal_aspects import NatalAspects
12
13
  from kerykeion.astrological_subject import AstrologicalSubject
13
- from kerykeion.kr_types import KerykeionException, ChartType
14
+ from kerykeion.kr_types import KerykeionException, ChartType, KerykeionPointModel, Sign
14
15
  from kerykeion.kr_types import ChartTemplateDictionary
15
- from kerykeion.kr_types.settings_models import KerykeionSettingsCelestialPointModel
16
+ from kerykeion.kr_types.kr_models import AstrologicalSubjectModel
17
+ from kerykeion.kr_types.settings_models import KerykeionSettingsCelestialPointModel, KerykeionSettingsModel
18
+ from kerykeion.kr_types.kr_literals import KerykeionChartTheme
16
19
  from kerykeion.charts.charts_utils import (
17
- degreeDiff,
18
- sliceToX,
19
- sliceToY,
20
20
  draw_zodiac_slice,
21
21
  convert_latitude_coordinate_to_string,
22
22
  convert_longitude_coordinate_to_string,
23
23
  draw_aspect_line,
24
24
  draw_elements_percentages,
25
- convert_decimal_to_degree_string,
26
25
  draw_transit_ring_degree_steps,
27
26
  draw_degree_ring,
28
27
  draw_transit_ring,
29
28
  draw_first_circle,
30
- draw_second_circle
29
+ draw_second_circle,
30
+ draw_third_circle,
31
+ draw_aspect_grid,
32
+ draw_houses_cusps_and_text_number,
33
+ draw_transit_aspect_list,
34
+ draw_transit_aspect_grid,
35
+ draw_moon_phase,
36
+ draw_house_grid,
37
+ draw_planet_grid,
31
38
  )
39
+ from kerykeion.charts.draw_planets import draw_planets # type: ignore
40
+ from kerykeion.utilities import get_houses_list
32
41
  from pathlib import Path
33
42
  from scour.scour import scourString
34
43
  from string import Template
35
- from typing import Union, List
44
+ from typing import Union, List, Literal
36
45
  from datetime import datetime
37
46
 
38
47
 
39
-
40
48
  class KerykeionChartSVG:
41
49
  """
42
50
  Creates the instance that can generate the chart with the
@@ -49,19 +57,26 @@ class KerykeionChartSVG:
49
57
  - new_output_directory: Set the output directory (default: home directory).
50
58
  - new_settings_file: Set the settings file (default: kr.config.json).
51
59
  In the settings file you can set the language, colors, planets, aspects, etc.
60
+ - theme: Set the theme for the chart (default: classic). If None the <style> tag will be empty.
61
+ That's useful if you want to use your own CSS file customizing the value of the default theme variables.
62
+ - double_chart_aspect_grid_type: Set the type of the aspect grid for the double chart (transit or synastry). (Default: list.)
63
+ - chart_language: Set the language for the chart (default: EN).
52
64
  """
53
65
 
54
66
  # Constants
67
+ _BASIC_CHART_VIEWBOX = "0 0 772.2 546.0"
68
+ _WIDE_CHART_VIEWBOX = "0 0 1060 546.0"
55
69
  _DEFAULT_HEIGHT = 546.0
56
70
  _DEFAULT_FULL_WIDTH = 1200
57
71
  _DEFAULT_NATAL_WIDTH = 772.2
72
+ _PLANET_IN_ZODIAC_EXTRA_POINTS = 10
58
73
 
59
74
  # Set at init
60
- first_obj: AstrologicalSubject
61
- second_obj: Union[AstrologicalSubject, None]
75
+ first_obj: Union[AstrologicalSubject, AstrologicalSubjectModel]
76
+ second_obj: Union[AstrologicalSubject, AstrologicalSubjectModel, None]
62
77
  chart_type: ChartType
63
78
  new_output_directory: Union[Path, None]
64
- new_settings_file: Union[Path, None]
79
+ new_settings_file: Union[Path, None, KerykeionSettingsModel, dict]
65
80
  output_directory: Path
66
81
 
67
82
  # Internal properties
@@ -69,58 +84,43 @@ class KerykeionChartSVG:
69
84
  earth: float
70
85
  air: float
71
86
  water: float
72
- c1: float
73
- c2: float
74
- c3: float
75
- homedir: Path
76
- xml_svg: Path
87
+ main_radius: float
88
+ first_circle_radius: float
89
+ second_circle_radius: float
90
+ third_circle_radius: float
77
91
  width: Union[float, int]
78
92
  language_settings: dict
79
93
  chart_colors_settings: dict
80
94
  planets_settings: dict
81
95
  aspects_settings: dict
82
- planet_in_zodiac_extra_points: int
83
- chart_settings: dict
84
- user: AstrologicalSubject
96
+ user: Union[AstrologicalSubject, AstrologicalSubjectModel]
85
97
  available_planets_setting: List[KerykeionSettingsCelestialPointModel]
86
- transit_ring_exclude_points_names: List[str]
87
- points_deg_ut: list
88
- points_deg: list
89
- points_sign: list
90
- points_retrograde: list
91
- houses_sign_graph: list
92
- t_points_deg_ut: list
93
- t_points_deg: list
94
- t_points_sign: list
95
- t_points_retrograde: list
96
- t_houses_sign_graph: list
97
98
  height: float
98
99
  location: str
99
100
  geolat: float
100
101
  geolon: float
101
- zoom: int
102
- zodiac: tuple
103
102
  template: str
104
103
 
105
104
  def __init__(
106
105
  self,
107
- first_obj: AstrologicalSubject,
106
+ first_obj: Union[AstrologicalSubject, AstrologicalSubjectModel],
108
107
  chart_type: ChartType = "Natal",
109
- second_obj: Union[AstrologicalSubject, None] = None,
108
+ second_obj: Union[AstrologicalSubject, AstrologicalSubjectModel, None] = None,
110
109
  new_output_directory: Union[str, None] = None,
111
- new_settings_file: Union[Path, None] = None,
110
+ new_settings_file: Union[Path, None, KerykeionSettingsModel, dict] = None,
111
+ theme: Union[KerykeionChartTheme, None] = "classic",
112
+ double_chart_aspect_grid_type: Literal["list", "table"] = "list",
113
+ chart_language: str = "EN",
112
114
  ):
113
115
  # Directories:
114
- DATA_DIR = Path(__file__).parent
115
- self.homedir = Path.home()
116
+ home_directory = Path.home()
116
117
  self.new_settings_file = new_settings_file
118
+ self.chart_language = chart_language
117
119
 
118
120
  if new_output_directory:
119
121
  self.output_directory = Path(new_output_directory)
120
122
  else:
121
- self.output_directory = self.homedir
122
-
123
- self.xml_svg = DATA_DIR / "templates/chart.xml"
123
+ self.output_directory = home_directory
124
124
 
125
125
  self.parse_json_settings(new_settings_file)
126
126
  self.chart_type = chart_type
@@ -135,58 +135,20 @@ class KerykeionChartSVG:
135
135
 
136
136
  self.available_planets_setting.append(body)
137
137
 
138
- # House cusp points are excluded from the transit ring.
139
- self.transit_ring_exclude_points_names = [
140
- "First_House",
141
- "Second_House",
142
- "Third_House",
143
- "Fourth_House",
144
- "Fifth_House",
145
- "Sixth_House",
146
- "Seventh_House",
147
- "Eighth_House",
148
- "Ninth_House",
149
- "Tenth_House",
150
- "Eleventh_House",
151
- "Twelfth_House"
152
- ]
153
-
154
138
  # Available bodies
155
- available_celestial_points = []
139
+ available_celestial_points_names = []
156
140
  for body in self.available_planets_setting:
157
- available_celestial_points.append(body["name"].lower())
158
-
159
- # Make a list for the absolute degrees of the points of the graphic.
160
- self.points_deg_ut = []
161
- for planet in available_celestial_points:
162
- self.points_deg_ut.append(self.user.get(planet).abs_pos)
163
-
164
- # Make a list of the relative degrees of the points in the graphic.
165
- self.points_deg = []
166
- for planet in available_celestial_points:
167
- self.points_deg.append(self.user.get(planet).position)
168
-
169
- # Make list of the points sign
170
- self.points_sign = []
171
- for planet in available_celestial_points:
172
- self.points_sign.append(self.user.get(planet).sign_num)
141
+ available_celestial_points_names.append(body["name"].lower())
173
142
 
174
- # Make a list of points if they are retrograde or not.
175
- self.points_retrograde = []
176
- for planet in available_celestial_points:
177
- self.points_retrograde.append(self.user.get(planet).retrograde)
143
+ self.available_kerykeion_celestial_points: list[KerykeionPointModel] = []
144
+ for body in available_celestial_points_names:
145
+ self.available_kerykeion_celestial_points.append(self.user.get(body))
178
146
 
179
147
  # Makes the sign number list.
180
-
181
- self.houses_sign_graph = []
182
- for h in self.user.houses_list:
183
- self.houses_sign_graph.append(h["sign_num"])
184
-
185
148
  if self.chart_type == "Natal" or self.chart_type == "ExternalNatal":
186
149
  natal_aspects_instance = NatalAspects(self.user, new_settings_file=self.new_settings_file)
187
150
  self.aspects_list = natal_aspects_instance.relevant_aspects
188
151
 
189
- # TODO: If not second should exit
190
152
  if self.chart_type == "Transit" or self.chart_type == "Synastry":
191
153
  if not second_obj:
192
154
  raise KerykeionException("Second object is required for Transit or Synastry charts.")
@@ -194,29 +156,15 @@ class KerykeionChartSVG:
194
156
  # Kerykeion instance
195
157
  self.t_user = second_obj
196
158
 
197
- # Make a list for the absolute degrees of the points of the graphic.
198
- self.t_points_deg_ut = []
199
- for planet in available_celestial_points:
200
- self.t_points_deg_ut.append(self.t_user.get(planet).abs_pos)
201
-
202
- # Make a list of the relative degrees of the points in the graphic.
203
- self.t_points_deg = []
204
- for planet in available_celestial_points:
205
- self.t_points_deg.append(self.t_user.get(planet).position)
159
+ # Aspects
160
+ self.aspects_list = SynastryAspects(self.user, self.t_user, new_settings_file=self.new_settings_file).relevant_aspects
206
161
 
207
- # Make list of the poits sign.
208
- self.t_points_sign = []
209
- for planet in available_celestial_points:
210
- self.t_points_sign.append(self.t_user.get(planet).sign_num)
162
+ self.t_available_kerykeion_celestial_points = []
163
+ for body in available_celestial_points_names:
164
+ self.t_available_kerykeion_celestial_points.append(self.t_user.get(body))
211
165
 
212
- # Make a list of poits if they are retrograde or not.
213
- self.t_points_retrograde = []
214
- for planet in available_celestial_points:
215
- self.t_points_retrograde.append(self.t_user.get(planet).retrograde)
216
-
217
- self.t_houses_sign_graph = []
218
- for h in self.t_user.houses_list:
219
- self.t_houses_sign_graph.append(h["sign_num"])
166
+ # Double chart aspect grid type
167
+ self.double_chart_aspect_grid_type = double_chart_aspect_grid_type
220
168
 
221
169
  # screen size
222
170
  self.height = self._DEFAULT_HEIGHT
@@ -230,31 +178,45 @@ class KerykeionChartSVG:
230
178
  self.geolat = self.user.lat
231
179
  self.geolon = self.user.lng
232
180
 
233
- logging.info(f"{self.user.name} birth location: {self.location}, {self.geolat}, {self.geolon}")
234
-
235
181
  if self.chart_type == "Transit":
236
182
  self.t_name = self.language_settings["transit_name"]
237
183
 
238
- # configuration
239
- # ZOOM 1 = 100%
240
- self.zoom = 1
241
-
242
- self.zodiac = (
243
- {"name": "aries", "element": "fire"},
244
- {"name": "taurus", "element": "earth"},
245
- {"name": "gemini", "element": "air"},
246
- {"name": "cancer", "element": "water"},
247
- {"name": "leo", "element": "fire"},
248
- {"name": "virgo", "element": "earth"},
249
- {"name": "libra", "element": "air"},
250
- {"name": "scorpio", "element": "water"},
251
- {"name": "sagittarius", "element": "fire"},
252
- {"name": "capricorn", "element": "earth"},
253
- {"name": "aquarius", "element": "air"},
254
- {"name": "pisces", "element": "water"},
255
- )
184
+ # Default radius for the chart
185
+ self.main_radius = 240
186
+
187
+ # Set circle radii based on chart type
188
+ if self.chart_type == "ExternalNatal":
189
+ self.first_circle_radius, self.second_circle_radius, self.third_circle_radius = 56, 92, 112
190
+ else:
191
+ self.first_circle_radius, self.second_circle_radius, self.third_circle_radius = 0, 36, 120
192
+
193
+ # Initialize element points
194
+ self.fire = 0.0
195
+ self.earth = 0.0
196
+ self.air = 0.0
197
+ self.water = 0.0
198
+
199
+ # Calculate element points from planets
200
+ self._calculate_elements_points_from_planets()
201
+
202
+ # Set up theme
203
+ if theme not in get_args(KerykeionChartTheme) and theme is not None:
204
+ raise KerykeionException(f"Theme {theme} is not available. Set None for default theme.")
205
+
206
+ self.set_up_theme(theme)
207
+
208
+ def set_up_theme(self, theme: Union[KerykeionChartTheme, None] = None) -> None:
209
+ """
210
+ Set the theme for the chart.
211
+ """
212
+ if theme is None:
213
+ self.color_style_tag = ""
214
+ return
215
+
216
+ theme_dir = Path(__file__).parent / "themes"
256
217
 
257
- self.template = None
218
+ with open(theme_dir / f"{theme}.css", "r") as f:
219
+ self.color_style_tag = f.read()
258
220
 
259
221
  def set_output_directory(self, dir_path: Path) -> None:
260
222
  """
@@ -263,19 +225,16 @@ class KerykeionChartSVG:
263
225
  self.output_directory = dir_path
264
226
  logging.info(f"Output direcotry set to: {self.output_directory}")
265
227
 
266
- def parse_json_settings(self, settings_file):
228
+ def parse_json_settings(self, settings_file_or_dict: Union[Path, dict, KerykeionSettingsModel, None]) -> None:
267
229
  """
268
230
  Parse the settings file.
269
231
  """
270
- settings = get_settings(settings_file)
232
+ settings = get_settings(settings_file_or_dict)
271
233
 
272
- language = settings["general_settings"]["language"]
273
- self.language_settings = settings["language_settings"].get(language, "EN")
234
+ self.language_settings = settings["language_settings"][self.chart_language]
274
235
  self.chart_colors_settings = settings["chart_colors"]
275
236
  self.planets_settings = settings["celestial_points"]
276
237
  self.aspects_settings = settings["aspects"]
277
- self.planet_in_zodiac_extra_points = settings["general_settings"]["planet_in_zodiac_extra_points"]
278
- self.chart_settings = settings["chart_settings"]
279
238
 
280
239
  def _draw_zodiac_circle_slices(self, r):
281
240
  """
@@ -288,124 +247,65 @@ class KerykeionChartSVG:
288
247
  Returns:
289
248
  str: The SVG string representing the zodiac circle.
290
249
  """
291
-
250
+ sings = get_args(Sign)
292
251
  output = ""
293
- for i, zodiac_element in enumerate(self.zodiac):
252
+ for i, sing in enumerate(sings):
294
253
  output += draw_zodiac_slice(
295
- c1=self.c1,
254
+ c1=self.first_circle_radius,
296
255
  chart_type=self.chart_type,
297
- seventh_house_degree_ut=self.user.houses_degree_ut[6],
256
+ seventh_house_degree_ut=self.user.seventh_house.abs_pos,
298
257
  num=i,
299
258
  r=r,
300
259
  style=f'fill:{self.chart_colors_settings[f"zodiac_bg_{i}"]}; fill-opacity: 0.5;',
301
- type=zodiac_element["name"],
260
+ type=sing,
302
261
  )
303
262
 
304
263
  return output
305
264
 
306
- def _makeHouses(self, r):
307
- path = ""
308
-
309
- xr = 12
310
- for i in range(xr):
311
- # check transit
312
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
313
- dropin = 160
314
- roff = 72
315
- t_roff = 36
316
- else:
317
- dropin = self.c3
318
- roff = self.c1
319
-
320
- # offset is negative desc houses_degree_ut[6]
321
- offset = (int(self.user.houses_degree_ut[int(xr / 2)]) / -1) + int(self.user.houses_degree_ut[i])
322
- x1 = sliceToX(0, (r - dropin), offset) + dropin
323
- y1 = sliceToY(0, (r - dropin), offset) + dropin
324
- x2 = sliceToX(0, r - roff, offset) + roff
325
- y2 = sliceToY(0, r - roff, offset) + roff
326
-
327
- if i < (xr - 1):
328
- text_offset = offset + int(degreeDiff(self.user.houses_degree_ut[(i + 1)], self.user.houses_degree_ut[i]) / 2)
329
- else:
330
- text_offset = offset + int(degreeDiff(self.user.houses_degree_ut[0], self.user.houses_degree_ut[(xr - 1)]) / 2)
331
-
332
- # mc, asc, dsc, ic
333
- if i == 0:
334
- linecolor = self.planets_settings[12]["color"]
335
- elif i == 9:
336
- linecolor = self.planets_settings[13]["color"]
337
- elif i == 6:
338
- linecolor = self.planets_settings[14]["color"]
339
- elif i == 3:
340
- linecolor = self.planets_settings[15]["color"]
341
- else:
342
- linecolor = self.chart_colors_settings["houses_radix_line"]
343
-
344
- # Transit houses lines.
345
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
346
- # Degrees for point zero.
347
-
348
- zeropoint = 360 - self.user.houses_degree_ut[6]
349
- t_offset = zeropoint + self.t_user.houses_degree_ut[i]
350
- if t_offset > 360:
351
- t_offset = t_offset - 360
352
- t_x1 = sliceToX(0, (r - t_roff), t_offset) + t_roff
353
- t_y1 = sliceToY(0, (r - t_roff), t_offset) + t_roff
354
- t_x2 = sliceToX(0, r, t_offset)
355
- t_y2 = sliceToY(0, r, t_offset)
356
- if i < 11:
357
- t_text_offset = t_offset + int(degreeDiff(self.t_user.houses_degree_ut[(i + 1)], self.t_user.houses_degree_ut[i]) / 2)
358
- else:
359
- t_text_offset = t_offset + int(degreeDiff(self.t_user.houses_degree_ut[0], self.t_user.houses_degree_ut[11]) / 2)
360
- # linecolor
361
- if i == 0 or i == 9 or i == 6 or i == 3:
362
- t_linecolor = linecolor
363
- else:
364
- t_linecolor = self.chart_colors_settings["houses_transit_line"]
365
- xtext = sliceToX(0, (r - 8), t_text_offset) + 8
366
- ytext = sliceToY(0, (r - 8), t_text_offset) + 8
367
-
368
- if self.chart_type == "Transit":
369
- path = path + '<text style="fill: #00f; fill-opacity: 0; font-size: 14px"><tspan x="' + str(xtext - 3) + '" y="' + str(ytext + 3) + '">' + str(i + 1) + "</tspan></text>"
370
- path = f"{path}<line x1='{str(t_x1)}' y1='{str(t_y1)}' x2='{str(t_x2)}' y2='{str(t_y2)}' style='stroke: {t_linecolor}; stroke-width: 2px; stroke-opacity:0;'/>"
371
-
372
- else:
373
- path = path + '<text style="fill: #00f; fill-opacity: .4; font-size: 14px"><tspan x="' + str(xtext - 3) + '" y="' + str(ytext + 3) + '">' + str(i + 1) + "</tspan></text>"
374
- path = f"{path}<line x1='{str(t_x1)}' y1='{str(t_y1)}' x2='{str(t_x2)}' y2='{str(t_y2)}' style='stroke: {t_linecolor}; stroke-width: 2px; stroke-opacity:.3;'/>"
375
-
376
-
377
- # if transit
378
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
379
- dropin = 84
380
- elif self.chart_type == "ExternalNatal":
381
- dropin = 100
382
- # Natal
383
- else:
384
- dropin = 48
385
-
386
- xtext = sliceToX(0, (r - dropin), text_offset) + dropin # was 132
387
- ytext = sliceToY(0, (r - dropin), text_offset) + dropin # was 132
388
- path = f'{path}<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke: {linecolor}; stroke-width: 2px; stroke-dasharray:3,2; stroke-opacity:.4;"/>'
389
- path = path + '<text style="fill: #f00; fill-opacity: .6; font-size: 14px"><tspan x="' + str(xtext - 3) + '" y="' + str(ytext + 3) + '">' + str(i + 1) + "</tspan></text>"
390
-
391
- return path
392
-
393
265
  def _calculate_elements_points_from_planets(self):
394
266
  """
395
267
  Calculate chart element points from a planet.
268
+ TODO: Refactor this method.
269
+ Should be completely rewritten. Maybe even part of the AstrologicalSubject class.
270
+ The points should include just the standard way of calculating the elements points.
396
271
  """
272
+
273
+ ZODIAC = (
274
+ {"name": "Ari", "element": "fire"},
275
+ {"name": "Tau", "element": "earth"},
276
+ {"name": "Gem", "element": "air"},
277
+ {"name": "Can", "element": "water"},
278
+ {"name": "Leo", "element": "fire"},
279
+ {"name": "Vir", "element": "earth"},
280
+ {"name": "Lib", "element": "air"},
281
+ {"name": "Sco", "element": "water"},
282
+ {"name": "Sag", "element": "fire"},
283
+ {"name": "Cap", "element": "earth"},
284
+ {"name": "Aqu", "element": "air"},
285
+ {"name": "Pis", "element": "water"},
286
+ )
397
287
 
288
+ # Available bodies
289
+ available_celestial_points_names = []
290
+ for body in self.available_planets_setting:
291
+ available_celestial_points_names.append(body["name"].lower())
292
+
293
+ # Make list of the points sign
294
+ points_sign = []
295
+ for planet in available_celestial_points_names:
296
+ points_sign.append(self.user.get(planet).sign_num)
297
+
398
298
  for i in range(len(self.available_planets_setting)):
399
299
  # element: get extra points if planet is in own zodiac sign.
400
300
  related_zodiac_signs = self.available_planets_setting[i]["related_zodiac_signs"]
401
- cz = self.points_sign[i]
301
+ cz = points_sign[i]
402
302
  extra_points = 0
403
303
  if related_zodiac_signs != []:
404
304
  for e in range(len(related_zodiac_signs)):
405
305
  if int(related_zodiac_signs[e]) == int(cz):
406
- extra_points = self.planet_in_zodiac_extra_points
306
+ extra_points = self._PLANET_IN_ZODIAC_EXTRA_POINTS
407
307
 
408
- ele = self.zodiac[self.points_sign[i]]["element"]
308
+ ele = ZODIAC[points_sign[i]]["element"]
409
309
  if ele == "fire":
410
310
  self.fire = self.fire + self.available_planets_setting[i]["element_points"] + extra_points
411
311
 
@@ -418,956 +318,223 @@ class KerykeionChartSVG:
418
318
  elif ele == "water":
419
319
  self.water = self.water + self.available_planets_setting[i]["element_points"] + extra_points
420
320
 
421
- def _make_planets(self, r):
422
- planets_degut = {}
423
- diff = range(len(self.available_planets_setting))
424
-
425
- for i in range(len(self.available_planets_setting)):
426
- # list of planets sorted by degree
427
- logging.debug(f"planet: {i}, degree: {self.points_deg_ut[i]}")
428
- planets_degut[self.points_deg_ut[i]] = i
429
-
430
- """
431
- FIXME: The planets_degut is a dictionary like:
432
- {planet_degree: planet_index}
433
- It should be replaced bu points_deg_ut
434
- print(self.points_deg_ut)
435
- print(planets_degut)
436
- """
437
-
438
- output = ""
439
- keys = list(planets_degut.keys())
440
- keys.sort()
441
- switch = 0
442
-
443
- planets_degrouped = {}
444
- groups = []
445
- planets_by_pos = list(range(len(planets_degut)))
446
- planet_drange = 3.4
447
- # get groups closely together
448
- group_open = False
449
- for e in range(len(keys)):
450
- i = planets_degut[keys[e]]
451
- # get distances between planets
452
- if e == 0:
453
- prev = self.points_deg_ut[planets_degut[keys[-1]]]
454
- next = self.points_deg_ut[planets_degut[keys[1]]]
455
- elif e == (len(keys) - 1):
456
- prev = self.points_deg_ut[planets_degut[keys[e - 1]]]
457
- next = self.points_deg_ut[planets_degut[keys[0]]]
458
- else:
459
- prev = self.points_deg_ut[planets_degut[keys[e - 1]]]
460
- next = self.points_deg_ut[planets_degut[keys[e + 1]]]
461
- diffa = degreeDiff(prev, self.points_deg_ut[i])
462
- diffb = degreeDiff(next, self.points_deg_ut[i])
463
- planets_by_pos[e] = [i, diffa, diffb]
464
-
465
- logging.debug(f'{self.available_planets_setting[i]["label"]}, {diffa}, {diffb}')
466
-
467
- if diffb < planet_drange:
468
- if group_open:
469
- groups[-1].append([e, diffa, diffb, self.available_planets_setting[planets_degut[keys[e]]]["label"]])
470
- else:
471
- group_open = True
472
- groups.append([])
473
- groups[-1].append([e, diffa, diffb, self.available_planets_setting[planets_degut[keys[e]]]["label"]])
474
- else:
475
- if group_open:
476
- groups[-1].append([e, diffa, diffb, self.available_planets_setting[planets_degut[keys[e]]]["label"]])
477
- group_open = False
478
-
479
- def zero(x):
480
- return 0
481
-
482
- planets_delta = list(map(zero, range(len(self.available_planets_setting))))
483
-
484
- # print groups
485
- # print planets_by_pos
486
- for a in range(len(groups)):
487
- # Two grouped planets
488
- if len(groups[a]) == 2:
489
- next_to_a = groups[a][0][0] - 1
490
- if groups[a][1][0] == (len(planets_by_pos) - 1):
491
- next_to_b = 0
492
- else:
493
- next_to_b = groups[a][1][0] + 1
494
- # if both planets have room
495
- if (groups[a][0][1] > (2 * planet_drange)) & (groups[a][1][2] > (2 * planet_drange)):
496
- planets_delta[groups[a][0][0]] = -(planet_drange - groups[a][0][2]) / 2
497
- planets_delta[groups[a][1][0]] = +(planet_drange - groups[a][0][2]) / 2
498
- # if planet a has room
499
- elif groups[a][0][1] > (2 * planet_drange):
500
- planets_delta[groups[a][0][0]] = -planet_drange
501
- # if planet b has room
502
- elif groups[a][1][2] > (2 * planet_drange):
503
- planets_delta[groups[a][1][0]] = +planet_drange
504
-
505
- # if planets next to a and b have room move them
506
- elif (planets_by_pos[next_to_a][1] > (2.4 * planet_drange)) & (planets_by_pos[next_to_b][2] > (2.4 * planet_drange)):
507
- planets_delta[(next_to_a)] = groups[a][0][1] - planet_drange * 2
508
- planets_delta[groups[a][0][0]] = -planet_drange * 0.5
509
- planets_delta[next_to_b] = -(groups[a][1][2] - planet_drange * 2)
510
- planets_delta[groups[a][1][0]] = +planet_drange * 0.5
511
-
512
- # if planet next to a has room move them
513
- elif planets_by_pos[next_to_a][1] > (2 * planet_drange):
514
- planets_delta[(next_to_a)] = groups[a][0][1] - planet_drange * 2.5
515
- planets_delta[groups[a][0][0]] = -planet_drange * 1.2
516
-
517
- # if planet next to b has room move them
518
- elif planets_by_pos[next_to_b][2] > (2 * planet_drange):
519
- planets_delta[next_to_b] = -(groups[a][1][2] - planet_drange * 2.5)
520
- planets_delta[groups[a][1][0]] = +planet_drange * 1.2
521
-
522
- # Three grouped planets or more
523
- xl = len(groups[a])
524
- if xl >= 3:
525
- available = groups[a][0][1]
526
- for f in range(xl):
527
- available += groups[a][f][2]
528
- need = (3 * planet_drange) + (1.2 * (xl - 1) * planet_drange)
529
- leftover = available - need
530
- xa = groups[a][0][1]
531
- xb = groups[a][(xl - 1)][2]
532
-
533
- # center
534
- if (xa > (need * 0.5)) & (xb > (need * 0.5)):
535
- startA = xa - (need * 0.5)
536
- # position relative to next planets
537
- else:
538
- startA = (leftover / (xa + xb)) * xa
539
- startB = (leftover / (xa + xb)) * xb
540
-
541
- if available > need:
542
- planets_delta[groups[a][0][0]] = startA - groups[a][0][1] + (1.5 * planet_drange)
543
- for f in range(xl - 1):
544
- planets_delta[groups[a][(f + 1)][0]] = 1.2 * planet_drange + planets_delta[groups[a][f][0]] - groups[a][f][2]
545
-
546
- for e in range(len(keys)):
547
- i = planets_degut[keys[e]]
548
-
549
- # coordinates
550
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
551
- if 22 < i < 27:
552
- rplanet = 76
553
- elif switch == 1:
554
- rplanet = 110
555
- switch = 0
556
- else:
557
- rplanet = 130
558
- switch = 1
559
- else:
560
- # if 22 < i < 27 it is asc,mc,dsc,ic (angles of chart)
561
- # put on special line (rplanet is range from outer ring)
562
- amin, bmin, cmin = 0, 0, 0
563
- if self.chart_type == "ExternalNatal":
564
- amin = 74 - 10
565
- bmin = 94 - 10
566
- cmin = 40 - 10
567
-
568
- if 22 < i < 27:
569
- rplanet = 40 - cmin
570
- elif switch == 1:
571
- rplanet = 74 - amin
572
- switch = 0
573
- else:
574
- rplanet = 94 - bmin
575
- switch = 1
576
-
577
- rtext = 45
578
-
579
- offset = (int(self.user.houses_degree_ut[6]) / -1) + int(self.points_deg_ut[i] + planets_delta[e])
580
- trueoffset = (int(self.user.houses_degree_ut[6]) / -1) + int(self.points_deg_ut[i])
581
-
582
- planet_x = sliceToX(0, (r - rplanet), offset) + rplanet
583
- planet_y = sliceToY(0, (r - rplanet), offset) + rplanet
584
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
585
- scale = 0.8
586
-
587
- elif self.chart_type == "ExternalNatal":
588
- scale = 0.8
589
- # line1
590
- x1 = sliceToX(0, (r - self.c3), trueoffset) + self.c3
591
- y1 = sliceToY(0, (r - self.c3), trueoffset) + self.c3
592
- x2 = sliceToX(0, (r - rplanet - 30), trueoffset) + rplanet + 30
593
- y2 = sliceToY(0, (r - rplanet - 30), trueoffset) + rplanet + 30
594
- color = self.available_planets_setting[i]["color"]
595
- output += (
596
- '<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke-width:1px;stroke:%s;stroke-opacity:.3;"/>\n'
597
- % (x1, y1, x2, y2, color)
598
- )
599
- # line2
600
- x1 = sliceToX(0, (r - rplanet - 30), trueoffset) + rplanet + 30
601
- y1 = sliceToY(0, (r - rplanet - 30), trueoffset) + rplanet + 30
602
- x2 = sliceToX(0, (r - rplanet - 10), offset) + rplanet + 10
603
- y2 = sliceToY(0, (r - rplanet - 10), offset) + rplanet + 10
604
- output += (
605
- '<line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke-width:1px;stroke:%s;stroke-opacity:.5;"/>\n'
606
- % (x1, y1, x2, y2, color)
607
- )
608
-
609
- else:
610
- scale = 1
611
- # output planet
612
- output += f'<g transform="translate(-{12 * scale},-{12 * scale})"><g transform="scale({scale})"><use x="{planet_x * (1/scale)}" y="{planet_y * (1/scale)}" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g></g>'
613
-
614
- # make transit degut and display planets
615
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
616
- group_offset = {}
617
- t_planets_degut = {}
618
- list_range = len(self.available_planets_setting)
619
-
620
- for i in range(list_range):
621
- if self.chart_type == "Transit" and self.available_planets_setting[i]['name'] in self.transit_ring_exclude_points_names:
622
- continue
623
-
624
- group_offset[i] = 0
625
- t_planets_degut[self.t_points_deg_ut[i]] = i
626
-
627
- t_keys = list(t_planets_degut.keys())
628
- t_keys.sort()
629
-
630
- # grab closely grouped planets
631
- groups = []
632
- in_group = False
633
- for e in range(len(t_keys)):
634
- i_a = t_planets_degut[t_keys[e]]
635
- if e == (len(t_keys) - 1):
636
- i_b = t_planets_degut[t_keys[0]]
637
- else:
638
- i_b = t_planets_degut[t_keys[e + 1]]
639
-
640
- a = self.t_points_deg_ut[i_a]
641
- b = self.t_points_deg_ut[i_b]
642
- diff = degreeDiff(a, b)
643
- if diff <= 2.5:
644
- if in_group:
645
- groups[-1].append(i_b)
646
- else:
647
- groups.append([i_a])
648
- groups[-1].append(i_b)
649
- in_group = True
650
- else:
651
- in_group = False
652
- # loop groups and set degrees display adjustment
653
- for i in range(len(groups)):
654
- if len(groups[i]) == 2:
655
- group_offset[groups[i][0]] = -1.0
656
- group_offset[groups[i][1]] = 1.0
657
- elif len(groups[i]) == 3:
658
- group_offset[groups[i][0]] = -1.5
659
- group_offset[groups[i][1]] = 0
660
- group_offset[groups[i][2]] = 1.5
661
- elif len(groups[i]) == 4:
662
- group_offset[groups[i][0]] = -2.0
663
- group_offset[groups[i][1]] = -1.0
664
- group_offset[groups[i][2]] = 1.0
665
- group_offset[groups[i][3]] = 2.0
666
-
667
- switch = 0
668
-
669
- # Transit planets loop
670
- for e in range(len(t_keys)):
671
- if self.chart_type == "Transit" and self.available_planets_setting[e]["name"] in self.transit_ring_exclude_points_names:
672
- continue
673
-
674
- i = t_planets_degut[t_keys[e]]
675
-
676
- if 22 < i < 27:
677
- rplanet = 9
678
- elif switch == 1:
679
- rplanet = 18
680
- switch = 0
681
- else:
682
- rplanet = 26
683
- switch = 1
684
-
685
- # Transit planet name
686
- zeropoint = 360 - self.user.houses_degree_ut[6]
687
- t_offset = zeropoint + self.t_points_deg_ut[i]
688
- if t_offset > 360:
689
- t_offset = t_offset - 360
690
- planet_x = sliceToX(0, (r - rplanet), t_offset) + rplanet
691
- planet_y = sliceToY(0, (r - rplanet), t_offset) + rplanet
692
- output += f'<g class="transit-planet-name" transform="translate(-6,-6)"><g transform="scale(0.5)"><use x="{planet_x*2}" y="{planet_y*2}" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g></g>'
693
-
694
- # Transit planet line
695
- x1 = sliceToX(0, r + 3, t_offset) - 3
696
- y1 = sliceToY(0, r + 3, t_offset) - 3
697
- x2 = sliceToX(0, r - 3, t_offset) + 3
698
- y2 = sliceToY(0, r - 3, t_offset) + 3
699
- output += f'<line class="transit-planet-line" x1="{str(x1)}" y1="{str(y1)}" x2="{str(x2)}" y2="{str(y2)}" style="stroke: {self.available_planets_setting[i]["color"]}; stroke-width: 1px; stroke-opacity:.8;"/>'
700
-
701
- # transit planet degree text
702
- rotate = self.user.houses_degree_ut[0] - self.t_points_deg_ut[i]
703
- textanchor = "end"
704
- t_offset += group_offset[i]
705
- rtext = -3.0
706
-
707
- if -90 > rotate > -270:
708
- rotate = rotate + 180.0
709
- textanchor = "start"
710
- if 270 > rotate > 90:
711
- rotate = rotate - 180.0
712
- textanchor = "start"
713
-
714
- if textanchor == "end":
715
- xo = 1
716
- else:
717
- xo = -1
718
- deg_x = sliceToX(0, (r - rtext), t_offset + xo) + rtext
719
- deg_y = sliceToY(0, (r - rtext), t_offset + xo) + rtext
720
- degree = int(t_offset)
721
- output += f'<g transform="translate({deg_x},{deg_y})">'
722
- output += f'<text transform="rotate({rotate})" text-anchor="{textanchor}'
723
- output += f'" style="fill: {self.available_planets_setting[i]["color"]}; font-size: 10px;">{convert_decimal_to_degree_string(self.t_points_deg[i], type="1")}'
724
- output += "</text></g>"
725
-
726
- # check transit
727
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
728
- dropin = 36
729
- else:
730
- dropin = 0
731
-
732
- # planet line
733
- x1 = sliceToX(0, r - (dropin + 3), offset) + (dropin + 3)
734
- y1 = sliceToY(0, r - (dropin + 3), offset) + (dropin + 3)
735
- x2 = sliceToX(0, (r - (dropin - 3)), offset) + (dropin - 3)
736
- y2 = sliceToY(0, (r - (dropin - 3)), offset) + (dropin - 3)
737
-
738
- output += f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke: {self.available_planets_setting[i]["color"]}; stroke-width: 2px; stroke-opacity:.6;"/>'
739
-
740
- # check transit
741
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
742
- dropin = 160
743
- else:
744
- dropin = 120
745
-
746
- x1 = sliceToX(0, r - dropin, offset) + dropin
747
- y1 = sliceToY(0, r - dropin, offset) + dropin
748
- x2 = sliceToX(0, (r - (dropin - 3)), offset) + (dropin - 3)
749
- y2 = sliceToY(0, (r - (dropin - 3)), offset) + (dropin - 3)
750
- output += f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke: {self.available_planets_setting[i]["color"]}; stroke-width: 2px; stroke-opacity:.6;"/>'
751
-
752
- return output
753
-
754
- def _makePatterns(self):
755
- """
756
- * Stellium: At least four planets linked together in a series of continuous conjunctions.
757
- * Grand trine: Three trine aspects together.
758
- * Grand cross: Two pairs of opposing planets squared to each other.
759
- * T-Square: Two planets in opposition squared to a third.
760
- * Yod: Two qunicunxes together joined by a sextile.
761
- """
762
- conj = {} # 0
763
- opp = {} # 10
764
- sq = {} # 5
765
- tr = {} # 6
766
- qc = {} # 9
767
- sext = {} # 3
768
- for i in range(len(self.available_planets_setting)):
769
- a = self.points_deg_ut[i]
770
- qc[i] = {}
771
- sext[i] = {}
772
- opp[i] = {}
773
- sq[i] = {}
774
- tr[i] = {}
775
- conj[i] = {}
776
- # skip some points
777
- n = self.available_planets_setting[i]["name"]
778
- if n == "earth" or n == "True_Node" or n == "osc. apogee" or n == "intp. apogee" or n == "intp. perigee":
779
- continue
780
- if n == "Dsc" or n == "Ic":
781
- continue
782
- for j in range(len(self.available_planets_setting)):
783
- # skip some points
784
- n = self.available_planets_setting[j]["name"]
785
- if n == "earth" or n == "True_Node" or n == "osc. apogee" or n == "intp. apogee" or n == "intp. perigee":
786
- continue
787
- if n == "Dsc" or n == "Ic":
788
- continue
789
- b = self.points_deg_ut[j]
790
- delta = float(degreeDiff(a, b))
791
- # check for opposition
792
- xa = float(self.aspects_settings[10]["degree"]) - float(self.aspects_settings[10]["orb"])
793
- xb = float(self.aspects_settings[10]["degree"]) + float(self.aspects_settings[10]["orb"])
794
- if xa <= delta <= xb:
795
- opp[i][j] = True
796
- # check for conjunction
797
- xa = float(self.aspects_settings[0]["degree"]) - float(self.aspects_settings[0]["orb"])
798
- xb = float(self.aspects_settings[0]["degree"]) + float(self.aspects_settings[0]["orb"])
799
- if xa <= delta <= xb:
800
- conj[i][j] = True
801
- # check for squares
802
- xa = float(self.aspects_settings[5]["degree"]) - float(self.aspects_settings[5]["orb"])
803
- xb = float(self.aspects_settings[5]["degree"]) + float(self.aspects_settings[5]["orb"])
804
- if xa <= delta <= xb:
805
- sq[i][j] = True
806
- # check for qunicunxes
807
- xa = float(self.aspects_settings[9]["degree"]) - float(self.aspects_settings[9]["orb"])
808
- xb = float(self.aspects_settings[9]["degree"]) + float(self.aspects_settings[9]["orb"])
809
- if xa <= delta <= xb:
810
- qc[i][j] = True
811
- # check for sextiles
812
- xa = float(self.aspects_settings[3]["degree"]) - float(self.aspects_settings[3]["orb"])
813
- xb = float(self.aspects_settings[3]["degree"]) + float(self.aspects_settings[3]["orb"])
814
- if xa <= delta <= xb:
815
- sext[i][j] = True
816
-
817
- yot = {}
818
- # check for double qunicunxes
819
- for k, v in qc.items():
820
- if len(qc[k]) >= 2:
821
- # check for sextile
822
- for l, w in qc[k].items():
823
- for m, x in qc[k].items():
824
- if m in sext[l]:
825
- if l > m:
826
- yot["%s,%s,%s" % (k, m, l)] = [k, m, l]
827
- else:
828
- yot["%s,%s,%s" % (k, l, m)] = [k, l, m]
829
- tsquare = {}
830
- # check for opposition
831
- for k, v in opp.items():
832
- if len(opp[k]) >= 1:
833
- # check for square
834
- for l, w in opp[k].items():
835
- for a, b in sq.items():
836
- if k in sq[a] and l in sq[a]:
837
- logging.debug(f"Got tsquare {a} {k} {l}")
838
- if k > l:
839
- tsquare[f"{a},{l},{k}"] = f"{self.available_planets_setting[a]['label']} => {self.available_planets_setting[l]['label']}, {self.available_planets_setting[k]['label']}"
840
-
841
- else:
842
- tsquare[f"{a},{k},{l}"] = f"{self.available_planets_setting[a]['label']} => {self.available_planets_setting[k]['label']}, {self.available_planets_setting[l]['label']}"
843
-
844
- stellium = {}
845
- # check for 4 continuous conjunctions
846
- for k, v in conj.items():
847
- if len(conj[k]) >= 1:
848
- # first conjunction
849
- for l, m in conj[k].items():
850
- if len(conj[l]) >= 1:
851
- for n, o in conj[l].items():
852
- # skip 1st conj
853
- if n == k:
854
- continue
855
- if len(conj[n]) >= 1:
856
- # third conjunction
857
- for p, q in conj[n].items():
858
- # skip first and second conj
859
- if p == k or p == n:
860
- continue
861
- if len(conj[p]) >= 1:
862
- # fourth conjunction
863
- for r, s in conj[p].items():
864
- # skip conj 1,2,3
865
- if r == k or r == n or r == p:
866
- continue
867
-
868
- l = [k, n, p, r]
869
- l.sort()
870
- stellium["%s %s %s %s" % (l[0], l[1], l[2], l[3])] = "%s %s %s %s" % (
871
- self.available_planets_setting[l[0]]["label"],
872
- self.available_planets_setting[l[1]]["label"],
873
- self.available_planets_setting[l[2]]["label"],
874
- self.available_planets_setting[l[3]]["label"],
875
- )
876
- # print yots
877
- out = '<g transform="translate(-30,380)">'
878
- if len(yot) >= 1:
879
- y = 0
880
- for k, v in yot.items():
881
- out += f'<text y="{y}" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 12px;">{"Yot"}</text>'
882
-
883
- # first planet symbol
884
- out += f'<g transform="translate(20,{y})">'
885
- out += f'<use transform="scale(0.4)" x="0" y="-20" xlink:href="#{self.available_planets_setting[yot[k][0]]["name"]}" /></g>'
886
-
887
- # second planet symbol
888
- out += f'<g transform="translate(30,{y})">'
889
- out += f'<use transform="scale(0.4)" x="0" y="-20" xlink:href="#{self.available_planets_setting[yot[k][1]]["name"]}" /></g>'
890
-
891
- # third planet symbol
892
- out += f'<g transform="translate(40,{y})">'
893
- out += f'<use transform="scale(0.4)" x="0" y="-20" xlink:href="#{self.available_planets_setting[yot[k][2]]["name"]}" /></g>'
894
-
895
- y = y + 14
896
- # finalize
897
- out += "</g>"
898
- # return out
899
- return ""
900
-
901
- # Aspect and aspect grid functions for natal type charts.
902
- def _makeAspects(self, r, ar):
321
+ def _draw_all_aspects_lines(self, r, ar):
903
322
  out = ""
904
- for element in self.aspects_list:
323
+ for aspect in self.aspects_list:
905
324
  out += draw_aspect_line(
906
325
  r=r,
907
326
  ar=ar,
908
- degA=element["p1_abs_pos"],
909
- degB=element["p2_abs_pos"],
910
- color=self.aspects_settings[element["aid"]]["color"],
327
+ aspect=aspect,
328
+ color=self.aspects_settings[aspect["aid"]]["color"],
911
329
  seventh_house_degree_ut=self.user.seventh_house.abs_pos
912
330
  )
913
331
 
914
332
  return out
915
333
 
916
- def _makeAspectGrid(self, r):
917
- out = ""
918
- style = "stroke:%s; stroke-width: 1px; stroke-opacity:.6; fill:none" % (self.chart_colors_settings["paper_0"])
919
- xindent = 380
920
- yindent = 468
921
- box = 14
922
- revr = list(range(len(self.available_planets_setting)))
923
- revr.reverse()
924
- counter = 0
925
- for a in revr:
926
- counter += 1
927
- out += f'<rect x="{xindent}" y="{yindent}" width="{box}" height="{box}" style="{style}"/>'
928
- out += f'<use transform="scale(0.4)" x="{(xindent+2)*2.5}" y="{(yindent+1)*2.5}" xlink:href="#{self.available_planets_setting[a]["name"]}" />'
929
-
930
- xindent = xindent + box
931
- yindent = yindent - box
932
- revr2 = list(range(a))
933
- revr2.reverse()
934
- xorb = xindent
935
- yorb = yindent + box
936
- for b in revr2:
937
- out += f'<rect x="{xorb}" y="{yorb}" width="{box}" height="{box}" style="{style}"/>'
938
-
939
- xorb = xorb + box
940
- for element in self.aspects_list:
941
- if (element["p1"] == a and element["p2"] == b) or (element["p1"] == b and element["p2"] == a):
942
- out += f'<use x="{xorb-box+1}" y="{yorb+1}" xlink:href="#orb{element["aspect_degrees"]}" />'
943
-
944
- return out
945
-
946
- # Aspect and aspect grid functions for transit type charts
947
- def _makeAspectsTransit(self, r, ar):
334
+ def _draw_all_transit_aspects_lines(self, r, ar):
948
335
  out = ""
949
-
950
- self.aspects_list = SynastryAspects(self.user, self.t_user, new_settings_file=self.new_settings_file).relevant_aspects
951
-
952
- for element in self.aspects_list:
336
+ for aspect in self.aspects_list:
953
337
  out += draw_aspect_line(
954
338
  r=r,
955
339
  ar=ar,
956
- degA=element["p1_abs_pos"],
957
- degB=element["p2_abs_pos"],
958
- color=self.aspects_settings[element["aid"]]["color"],
340
+ aspect=aspect,
341
+ color=self.aspects_settings[aspect["aid"]]["color"],
959
342
  seventh_house_degree_ut=self.user.seventh_house.abs_pos
960
343
  )
961
-
962
- return out
963
-
964
- def _makeAspectTransitGrid(self, r):
965
- out = '<g transform="translate(500,310)">'
966
- out += f'<text y="-15" x="0" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 14px;">{self.language_settings["aspects"]}:</text>'
967
-
968
- line = 0
969
- nl = 0
970
-
971
- for i in range(len(self.aspects_list)):
972
- if i == 12:
973
- nl = 100
974
-
975
- line = 0
976
-
977
- elif i == 24:
978
- nl = 200
979
-
980
- line = 0
981
-
982
- elif i == 36:
983
- nl = 300
984
-
985
- line = 0
986
-
987
- elif i == 48:
988
- nl = 400
989
-
990
- # When there are more than 60 aspects, the text is moved up
991
- if len(self.aspects_list) > 60:
992
- line = -1 * (len(self.aspects_list) - 60) * 14
993
- else:
994
- line = 0
995
-
996
- out += f'<g transform="translate({nl},{line})">'
997
-
998
- # first planet symbol
999
- out += f'<use transform="scale(0.4)" x="0" y="3" xlink:href="#{self.planets_settings[self.aspects_list[i]["p1"]]["name"]}" />'
1000
-
1001
- # aspect symbol
1002
- out += f'<use x="15" y="0" xlink:href="#orb{self.aspects_settings[self.aspects_list[i]["aid"]]["degree"]}" />'
1003
-
1004
- # second planet symbol
1005
- out += '<g transform="translate(30,0)">'
1006
- out += '<use transform="scale(0.4)" x="0" y="3" xlink:href="#%s" />' % (self.planets_settings[self.aspects_list[i]["p2"]]["name"])
1007
-
1008
- out += "</g>"
1009
- # difference in degrees
1010
- out += f'<text y="8" x="45" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{convert_decimal_to_degree_string(self.aspects_list[i]["orbit"])}</text>'
1011
- # line
1012
- out += "</g>"
1013
- line = line + 14
1014
- out += "</g>"
1015
- return out
1016
-
1017
- def _makePlanetGrid(self):
1018
- li = 10
1019
- offset = 0
1020
-
1021
- out = '<g transform="translate(510,-20)">'
1022
- out += '<g transform="translate(140, -15)">'
1023
- out += f'<text text-anchor="end" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 14px;">{self.language_settings["planets_and_house"]} {self.user.name}:</text>'
1024
- out += "</g>"
1025
-
1026
- end_of_line = None
1027
- for i in range(len(self.available_planets_setting)):
1028
- offset_between_lines = 14
1029
- end_of_line = "</g>"
1030
-
1031
- # Guarda qui !!
1032
- if i == 27:
1033
- li = 10
1034
- offset = -120
1035
-
1036
- # start of line
1037
- out += f'<g transform="translate({offset},{li})">'
1038
-
1039
- # planet text
1040
- out += f'<text text-anchor="end" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{self.language_settings["celestial_points"][self.available_planets_setting[i]["label"]]}</text>'
1041
-
1042
- # planet symbol
1043
- out += f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g>'
1044
-
1045
- # planet degree
1046
- out += f'<text text-anchor="start" x="19" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{convert_decimal_to_degree_string(self.points_deg[i])}</text>'
1047
-
1048
- # zodiac
1049
- out += f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.points_sign[i]]["name"]}" /></g>'
1050
-
1051
- # planet retrograde
1052
- if self.points_retrograde[i]:
1053
- out += '<g transform="translate(74,-6)"><use transform="scale(.5)" xlink:href="#retrograde" /></g>'
1054
-
1055
- # end of line
1056
- out += end_of_line
1057
-
1058
- li = li + offset_between_lines
1059
-
1060
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
1061
- if self.chart_type == "Transit":
1062
- out += '<g transform="translate(320, -15)">'
1063
- out += f'<text text-anchor="end" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 14px;">{self.t_name}:</text>'
1064
- else:
1065
- out += '<g transform="translate(380, -15)">'
1066
- out += f'<text text-anchor="end" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 14px;">{self.language_settings["planets_and_house"]} {self.t_user.name}:</text>'
1067
-
1068
- out += end_of_line
1069
-
1070
- t_li = 10
1071
- t_offset = 250
1072
-
1073
- for i in range(len(self.available_planets_setting)):
1074
- if i == 27:
1075
- t_li = 10
1076
- t_offset = -120
1077
-
1078
- # start of line
1079
- out += f'<g transform="translate({t_offset},{t_li})">'
1080
-
1081
- # planet text
1082
- out += f'<text text-anchor="end" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{self.language_settings["celestial_points"][self.available_planets_setting[i]["label"]]}</text>'
1083
- # planet symbol
1084
- out += f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g>'
1085
- # planet degree
1086
- out += f'<text text-anchor="start" x="19" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{convert_decimal_to_degree_string(self.t_points_deg[i])}</text>'
1087
- # zodiac
1088
- out += f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.t_points_sign[i]]["name"]}" /></g>'
1089
-
1090
- # planet retrograde
1091
- if self.t_points_retrograde[i]:
1092
- out += '<g transform="translate(74,-6)"><use transform="scale(.5)" xlink:href="#retrograde" /></g>'
1093
-
1094
- # end of line
1095
- out += end_of_line
1096
-
1097
- t_li = t_li + offset_between_lines
1098
-
1099
- if end_of_line is None:
1100
- raise KerykeionException("End of line not found")
1101
-
1102
- out += end_of_line
1103
344
  return out
1104
345
 
1105
- def _draw_house_grid(self):
346
+ def _create_template_dictionary(self) -> ChartTemplateDictionary:
1106
347
  """
1107
- Generate SVG code for a grid of astrological houses.
348
+ Create a dictionary containing the template data for generating an astrological chart.
1108
349
 
1109
350
  Returns:
1110
- str: The SVG code for the grid of houses.
351
+ ChartTemplateDictionary: A dictionary with template data for the chart.
1111
352
  """
1112
- out = '<g transform="translate(610,-20)">'
1113
-
1114
- li = 10
1115
- for i in range(12):
1116
- if i < 9:
1117
- cusp = "&#160;&#160;" + str(i + 1)
1118
- else:
1119
- cusp = str(i + 1)
1120
- out += f'<g transform="translate(0,{li})">'
1121
- out += f'<text text-anchor="end" x="40" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{self.language_settings["cusp"]} {cusp}:</text>'
1122
- out += f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.houses_sign_graph[i]]["name"]}" /></g>'
1123
- out += f'<text x="53" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;"> {convert_decimal_to_degree_string(self.user.houses_list[i]["position"])}</text>'
1124
- out += "</g>"
1125
- li = li + 14
353
+ # Initialize template dictionary
354
+ template_dict: ChartTemplateDictionary = dict() # type: ignore
1126
355
 
1127
- out += "</g>"
1128
-
1129
- if self.chart_type == "Synastry":
1130
- out += '<!-- Synastry Houses -->'
1131
- out += '<g transform="translate(850, -20)">'
1132
- li = 10
1133
-
1134
- for i in range(12):
1135
- if i < 9:
1136
- cusp = "&#160;&#160;" + str(i + 1)
1137
- else:
1138
- cusp = str(i + 1)
1139
- out += '<g transform="translate(0,' + str(li) + ')">'
1140
- out += f'<text text-anchor="end" x="40" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{self.language_settings["cusp"]} {cusp}:</text>'
1141
- out += f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.t_houses_sign_graph[i]]["name"]}" /></g>'
1142
- out += f'<text x="53" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;"> {convert_decimal_to_degree_string(self.t_user.houses_list[i]["position"])}</text>'
1143
- out += "</g>"
1144
- li = li + 14
1145
- out += "</g>"
1146
-
1147
- return out
1148
-
1149
- def _createTemplateDictionary(self) -> ChartTemplateDictionary:
1150
- # self.chart_type = "Transit"
1151
- # empty element points
1152
- self.fire = 0.0
1153
- self.earth = 0.0
1154
- self.air = 0.0
1155
- self.water = 0.0
1156
-
1157
- # Calculate the elements points
1158
- self._calculate_elements_points_from_planets()
1159
-
1160
- # Viewbox and sizing
1161
- svgHeight = "100%"
1162
- svgWidth = "100%"
1163
- rotate = "0"
1164
-
1165
- # To increase the size of the chart, change the viewbox
1166
- if self.chart_type == "Natal" or self.chart_type == "ExternalNatal":
1167
- viewbox = self.chart_settings["basic_chart_viewBox"]
1168
- else:
1169
- viewbox = self.chart_settings["wide_chart_viewBox"]
356
+ # Set the color style tag
357
+ template_dict["color_style_tag"] = self.color_style_tag
1170
358
 
1171
- # template dictionary
1172
- td: ChartTemplateDictionary = dict() # type: ignore
1173
- r = 240
359
+ # Set chart dimensions
360
+ template_dict["chart_height"] = self.height
361
+ template_dict["chart_width"] = self.width
1174
362
 
1175
- if self.chart_type == "ExternalNatal":
1176
- self.c1 = 56
1177
- self.c2 = 92
1178
- self.c3 = 112
1179
- else:
1180
- self.c1 = 0
1181
- self.c2 = 36
1182
- self.c3 = 120
363
+ # Set chart name
364
+ template_dict["stringName"] = f"{self.user.name}:" if self.chart_type in ["Synastry", "Transit"] else f'{self.language_settings["info"]}:'
1183
365
 
1184
- # transit
1185
- if self.chart_type == "Transit" or self.chart_type == "Synastry":
1186
- td["transitRing"] = draw_transit_ring(r, self.chart_colors_settings["paper_1"], self.chart_colors_settings["zodiac_transit_ring_3"])
1187
- td["degreeRing"] = draw_transit_ring_degree_steps(r, self.user.seventh_house.abs_pos)
366
+ # Set viewbox based on chart type
367
+ template_dict['viewbox'] = self._BASIC_CHART_VIEWBOX if self.chart_type in ["Natal", "ExternalNatal"] else self._WIDE_CHART_VIEWBOX
1188
368
 
1189
- # circles
1190
- td["first_circle"] = draw_first_circle(r, self.chart_colors_settings["zodiac_transit_ring_2"], self.chart_type)
1191
- td["second_circle"] = draw_second_circle(r, self.chart_colors_settings['zodiac_transit_ring_1'], self.chart_colors_settings['paper_1'], self.chart_type)
369
+ # Generate rings and circles based on chart type
370
+ if self.chart_type in ["Transit", "Synastry"]:
371
+ template_dict["transitRing"] = draw_transit_ring(self.main_radius, self.chart_colors_settings["paper_1"], self.chart_colors_settings["zodiac_transit_ring_3"])
372
+ template_dict["degreeRing"] = draw_transit_ring_degree_steps(self.main_radius, self.user.seventh_house.abs_pos)
373
+ template_dict["first_circle"] = draw_first_circle(self.main_radius, self.chart_colors_settings["zodiac_transit_ring_2"], self.chart_type)
374
+ template_dict["second_circle"] = draw_second_circle(self.main_radius, self.chart_colors_settings['zodiac_transit_ring_1'], self.chart_colors_settings['paper_1'], self.chart_type)
375
+ template_dict['third_circle'] = draw_third_circle(self.main_radius, self.chart_colors_settings['zodiac_transit_ring_0'], self.chart_colors_settings['paper_1'], self.chart_type, self.third_circle_radius)
1192
376
 
1193
- td["c3"] = 'cx="' + str(r) + '" cy="' + str(r) + '" r="' + str(r - 160) + '"'
1194
- td["c3style"] = f"fill: {self.chart_colors_settings['paper_1']}; fill-opacity:.8; stroke: {self.chart_colors_settings['zodiac_transit_ring_0']}; stroke-width: 1px"
377
+ if self.double_chart_aspect_grid_type == "list":
378
+ template_dict["makeAspectGrid"] = draw_transit_aspect_list(self.language_settings["aspects"], self.aspects_list, self.planets_settings, self.aspects_settings)
379
+ else:
380
+ template_dict["makeAspectGrid"] = draw_transit_aspect_grid(self.chart_colors_settings['paper_0'], self.available_planets_setting, self.aspects_list, 550, 450)
1195
381
 
1196
- td["makeAspects"] = self._makeAspectsTransit(r, (r - 160))
1197
- td["makeAspectGrid"] = self._makeAspectTransitGrid(r)
1198
- td["makePatterns"] = ""
382
+ template_dict["makeAspects"] = self._draw_all_transit_aspects_lines(self.main_radius, self.main_radius - 160)
1199
383
  else:
1200
- td["transitRing"] = ""
1201
- td["degreeRing"] = draw_degree_ring(r, self.c1, self.user.seventh_house.abs_pos, self.chart_colors_settings["paper_0"])
384
+ template_dict["transitRing"] = ""
385
+ template_dict["degreeRing"] = draw_degree_ring(self.main_radius, self.first_circle_radius, self.user.seventh_house.abs_pos, self.chart_colors_settings["paper_0"])
386
+ template_dict['first_circle'] = draw_first_circle(self.main_radius, self.chart_colors_settings["zodiac_radix_ring_2"], self.chart_type, self.first_circle_radius)
387
+ template_dict["second_circle"] = draw_second_circle(self.main_radius, self.chart_colors_settings["zodiac_radix_ring_1"], self.chart_colors_settings["paper_1"], self.chart_type, self.second_circle_radius)
388
+ template_dict['third_circle'] = draw_third_circle(self.main_radius, self.chart_colors_settings["zodiac_radix_ring_0"], self.chart_colors_settings["paper_1"], self.chart_type, self.third_circle_radius)
389
+ template_dict["makeAspectGrid"] = draw_aspect_grid(self.chart_colors_settings['paper_0'], self.available_planets_setting, self.aspects_list)
1202
390
 
1203
- td['first_circle'] = draw_first_circle(r, self.chart_colors_settings["zodiac_radix_ring_2"], self.chart_type, self.c1)
391
+ template_dict["makeAspects"] = self._draw_all_aspects_lines(self.main_radius, self.main_radius - self.third_circle_radius)
1204
392
 
1205
- td["second_circle"] = draw_second_circle(r, self.chart_colors_settings["zodiac_radix_ring_1"], self.chart_colors_settings["paper_1"], self.chart_type, self.c2)
1206
-
1207
- td["c3"] = f'cx="{r}" cy="{r}" r="{r - self.c3}"'
1208
- td["c3style"] = f'fill: {self.chart_colors_settings["paper_1"]}; fill-opacity:.8; stroke: {self.chart_colors_settings["zodiac_radix_ring_0"]}; stroke-width: 1px'
1209
-
1210
- td["makeAspects"] = self._makeAspects(r, (r - self.c3))
1211
- td["makeAspectGrid"] = self._makeAspectGrid(r)
1212
- td["makePatterns"] = self._makePatterns()
1213
-
1214
- td["chart_height"] = self.height
1215
- td["chart_width"] = self.width
1216
- td["circleX"] = str(0)
1217
- td["circleY"] = str(0)
1218
- td["svgWidth"] = str(svgWidth)
1219
- td["svgHeight"] = str(svgHeight)
1220
- td["viewbox"] = viewbox
1221
-
1222
- # Chart Title
393
+ # Set chart title
1223
394
  if self.chart_type == "Synastry":
1224
- td["stringTitle"] = f"{self.user.name} {self.language_settings['and_word']} {self.t_user.name}"
1225
-
395
+ template_dict["stringTitle"] = f"{self.user.name} {self.language_settings['and_word']} {self.t_user.name}"
1226
396
  elif self.chart_type == "Transit":
1227
- td["stringTitle"] = f"{self.language_settings['transits']} {self.t_user.day}/{self.t_user.month}/{self.t_user.year}"
1228
-
397
+ template_dict["stringTitle"] = f"{self.language_settings['transits']} {self.t_user.day}/{self.t_user.month}/{self.t_user.year}"
1229
398
  else:
1230
- td["stringTitle"] = self.user.name
1231
-
1232
- # Chart Name
1233
- if self.chart_type == "Synastry" or self.chart_type == "Transit":
1234
- td["stringName"] = f"{self.user.name}:"
399
+ template_dict["stringTitle"] = self.user.name
400
+
401
+ # Set bottom left corner information
402
+ template_dict["bottomLeft0"] = f"{self.user.zodiac_type if self.user.zodiac_type == 'Tropic' else str(self.user.zodiac_type) + ' ' + str(self.user.sidereal_mode)}"
403
+ template_dict["bottomLeft1"] = f"{self.user.houses_system_name}"
404
+ if self.chart_type in ["Natal", "ExternalNatal", "Synastry"]:
405
+ template_dict["bottomLeft2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get("day", "Day")} {self.user.lunar_phase.get("moon_phase", "")}'
406
+ template_dict["bottomLeft3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.user.lunar_phase.moon_phase_name}'
407
+ template_dict["bottomLeft4"] = f'{self.user.perspective_type}'
1235
408
  else:
1236
- td["stringName"] = f'{self.language_settings["info"]}:'
1237
-
1238
- # Bottom Left Corner
1239
- if self.chart_type == "Natal" or self.chart_type == "ExternalNatal" or self.chart_type == "Synastry":
1240
- td["bottomLeft0"] = f"{self.user.zodiac_type if self.user.zodiac_type == 'Tropic' else self.user.zodiac_type + ' ' + self.user.sidereal_mode}"
1241
- td["bottomLeft1"] = f"{self.user.houses_system_name}"
1242
- td["bottomLeft2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get("day", "Day")} {self.user.lunar_phase.get("moon_phase", "")}'
1243
- td["bottomLeft3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.user.lunar_phase.moon_phase_name}'
1244
- td["bottomLeft4"] = f'{self.user.perspective_type}'
409
+ template_dict["bottomLeft2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get("day", "Day")} {self.t_user.lunar_phase.get("moon_phase", "")}'
410
+ template_dict["bottomLeft3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.t_user.lunar_phase.moon_phase_name}'
411
+ template_dict["bottomLeft4"] = f'{self.t_user.perspective_type}'
412
+
413
+ # Draw moon phase
414
+ template_dict['moon_phase'] = draw_moon_phase(
415
+ self.user.lunar_phase["degrees_between_s_m"],
416
+ self.geolat
417
+ )
1245
418
 
1246
- else:
1247
- td["bottomLeft0"] = f"{self.user.zodiac_type if self.user.zodiac_type == 'Tropic' else self.user.zodiac_type + ' ' + self.user.sidereal_mode}"
1248
- td["bottomLeft1"] = f"{self.user.houses_system_name}"
1249
- td["bottomLeft2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get("day", "Day")} {self.t_user.lunar_phase.get("moon_phase", "")}'
1250
- td["bottomLeft3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.t_user.lunar_phase.moon_phase_name}'
1251
- td["bottomLeft4"] = f'{self.t_user.perspective_type}'
1252
-
1253
- # lunar phase
1254
- deg = self.user.lunar_phase["degrees_between_s_m"]
1255
-
1256
- lffg = None
1257
- lfbg = None
1258
- lfcx = None
1259
- lfr = None
1260
-
1261
- if deg < 90.0:
1262
- maxr = deg
1263
- if deg > 80.0:
1264
- maxr = maxr * maxr
1265
- lfcx = 20.0 + (deg / 90.0) * (maxr + 10.0)
1266
- lfr = 10.0 + (deg / 90.0) * maxr
1267
- lffg = self.chart_colors_settings["lunar_phase_0"]
1268
- lfbg = self.chart_colors_settings["lunar_phase_1"]
1269
-
1270
- elif deg < 180.0:
1271
- maxr = 180.0 - deg
1272
- if deg < 100.0:
1273
- maxr = maxr * maxr
1274
- lfcx = 20.0 + ((deg - 90.0) / 90.0 * (maxr + 10.0)) - (maxr + 10.0)
1275
- lfr = 10.0 + maxr - ((deg - 90.0) / 90.0 * maxr)
1276
- lffg = self.chart_colors_settings["lunar_phase_1"]
1277
- lfbg = self.chart_colors_settings["lunar_phase_0"]
1278
-
1279
- elif deg < 270.0:
1280
- maxr = deg - 180.0
1281
- if deg > 260.0:
1282
- maxr = maxr * maxr
1283
- lfcx = 20.0 + ((deg - 180.0) / 90.0 * (maxr + 10.0))
1284
- lfr = 10.0 + ((deg - 180.0) / 90.0 * maxr)
1285
- lffg, lfbg = self.chart_colors_settings["lunar_phase_1"], self.chart_colors_settings["lunar_phase_0"]
1286
-
1287
- elif deg < 361:
1288
- maxr = 360.0 - deg
1289
- if deg < 280.0:
1290
- maxr = maxr * maxr
1291
- lfcx = 20.0 + ((deg - 270.0) / 90.0 * (maxr + 10.0)) - (maxr + 10.0)
1292
- lfr = 10.0 + maxr - ((deg - 270.0) / 90.0 * maxr)
1293
- lffg, lfbg = self.chart_colors_settings["lunar_phase_0"], self.chart_colors_settings["lunar_phase_1"]
1294
-
1295
- if lffg is None or lfbg is None or lfcx is None or lfr is None:
1296
- raise KerykeionException("Lunar phase error")
1297
-
1298
- td["lunar_phase_fg"] = lffg
1299
- td["lunar_phase_bg"] = lfbg
1300
- td["lunar_phase_cx"] = lfcx
1301
- td["lunar_phase_r"] = lfr
1302
- td["lunar_phase_outline"] = self.chart_colors_settings["lunar_phase_2"]
1303
-
1304
- # rotation based on latitude
1305
- td["lunar_phase_rotate"] = -90.0 - self.geolat
1306
-
1307
- # stringlocation
419
+ # Set location string
1308
420
  if len(self.location) > 35:
1309
- split = self.location.split(",")
1310
- if len(split) > 1:
1311
- td["stringLocation"] = split[0] + ", " + split[-1]
1312
- if len(td["stringLocation"]) > 35:
1313
- td["stringLocation"] = td["stringLocation"][:35] + "..."
421
+ split_location = self.location.split(",")
422
+ if len(split_location) > 1:
423
+ template_dict["stringLocation"] = split_location[0] + ", " + split_location[-1]
424
+ if len(template_dict["stringLocation"]) > 35:
425
+ template_dict["stringLocation"] = template_dict["stringLocation"][:35] + "..."
1314
426
  else:
1315
- td["stringLocation"] = self.location[:35] + "..."
427
+ template_dict["stringLocation"] = self.location[:35] + "..."
1316
428
  else:
1317
- td["stringLocation"] = self.location
429
+ template_dict["stringLocation"] = self.location
1318
430
 
431
+ # Set additional information for Synastry chart type
1319
432
  if self.chart_type == "Synastry":
1320
- td["stringLat"] = f"{self.t_user.name}: "
1321
- td["stringLon"] = self.t_user.city
1322
- td["stringPosition"] = f"{self.t_user.year}-{self.t_user.month}-{self.t_user.day} {self.t_user.hour:02d}:{self.t_user.minute:02d}"
1323
-
433
+ template_dict["stringLat"] = f"{self.t_user.name}: "
434
+ template_dict["stringLon"] = self.t_user.city
435
+ template_dict["stringPosition"] = f"{self.t_user.year}-{self.t_user.month}-{self.t_user.day} {self.t_user.hour:02d}:{self.t_user.minute:02d}"
1324
436
  else:
1325
- latitude_string = convert_latitude_coordinate_to_string(
1326
- self.geolat,
1327
- self.language_settings['north'],
1328
- self.language_settings['south']
437
+ latitude_string = convert_latitude_coordinate_to_string(self.geolat, self.language_settings['north'], self.language_settings['south'])
438
+ longitude_string = convert_longitude_coordinate_to_string(self.geolon, self.language_settings['east'], self.language_settings['west'])
439
+ template_dict["stringLat"] = f"{self.language_settings['latitude']}: {latitude_string}"
440
+ template_dict["stringLon"] = f"{self.language_settings['longitude']}: {longitude_string}"
441
+ template_dict["stringPosition"] = f"{self.language_settings['type']}: {self.chart_type}"
442
+
443
+ # Set paper colors
444
+ template_dict["paper_color_0"] = self.chart_colors_settings["paper_0"]
445
+ template_dict["paper_color_1"] = self.chart_colors_settings["paper_1"]
446
+
447
+ # Set planet colors
448
+ for planet in self.planets_settings:
449
+ planet_id = planet["id"]
450
+ template_dict[f"planets_color_{planet_id}"] = planet["color"] # type: ignore
451
+
452
+ # Set zodiac colors
453
+ for i in range(12):
454
+ template_dict[f"zodiac_color_{i}"] = self.chart_colors_settings[f"zodiac_icon_{i}"] # type: ignore
455
+
456
+ # Set orb colors
457
+ for aspect in self.aspects_settings:
458
+ template_dict[f"orb_color_{aspect['degree']}"] = aspect['color'] # type: ignore
459
+
460
+ # Drawing functions
461
+ template_dict["makeZodiac"] = self._draw_zodiac_circle_slices(self.main_radius)
462
+
463
+ first_subject_houses_list = get_houses_list(self.user)
464
+
465
+ # Draw houses grid and cusps
466
+ if self.chart_type in ["Transit", "Synastry"]:
467
+ second_subject_houses_list = get_houses_list(self.t_user)
468
+
469
+ template_dict["makeHousesGrid"] = draw_house_grid(
470
+ main_subject_houses_list=first_subject_houses_list,
471
+ secondary_subject_houses_list=second_subject_houses_list,
472
+ chart_type=self.chart_type,
473
+ text_color=self.chart_colors_settings["paper_0"],
474
+ house_cusp_generale_name_label=self.language_settings["cusp"]
1329
475
  )
1330
- longitude_string = convert_longitude_coordinate_to_string(
1331
- self.geolon,
1332
- self.language_settings['east'],
1333
- self.language_settings['west']
476
+
477
+ template_dict["makeHouses"] = draw_houses_cusps_and_text_number(
478
+ r=self.main_radius,
479
+ first_subject_houses_list=first_subject_houses_list,
480
+ standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
481
+ first_house_color=self.planets_settings[12]["color"],
482
+ tenth_house_color=self.planets_settings[13]["color"],
483
+ seventh_house_color=self.planets_settings[14]["color"],
484
+ fourth_house_color=self.planets_settings[15]["color"],
485
+ c1=self.first_circle_radius,
486
+ c3=self.third_circle_radius,
487
+ chart_type=self.chart_type,
488
+ second_subject_houses_list=second_subject_houses_list,
489
+ transit_house_cusp_color=self.chart_colors_settings["houses_transit_line"],
1334
490
  )
1335
491
 
1336
- td["stringLat"] = f"{self.language_settings['latitude']}: {latitude_string}"
1337
- td["stringLon"] = f"{self.language_settings['longitude']}: {longitude_string}"
1338
- td["stringPosition"] = f"{self.language_settings['type']}: {self.chart_type}"
492
+ else:
493
+ template_dict["makeHousesGrid"] = draw_house_grid(
494
+ main_subject_houses_list=first_subject_houses_list,
495
+ chart_type=self.chart_type,
496
+ text_color=self.chart_colors_settings["paper_0"],
497
+ house_cusp_generale_name_label=self.language_settings["cusp"]
498
+ )
1339
499
 
1340
- # paper_color_X
1341
- td["paper_color_0"] = self.chart_colors_settings["paper_0"]
1342
- td["paper_color_1"] = self.chart_colors_settings["paper_1"]
500
+ template_dict["makeHouses"] = draw_houses_cusps_and_text_number(
501
+ r=self.main_radius,
502
+ first_subject_houses_list=first_subject_houses_list,
503
+ standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
504
+ first_house_color=self.planets_settings[12]["color"],
505
+ tenth_house_color=self.planets_settings[13]["color"],
506
+ seventh_house_color=self.planets_settings[14]["color"],
507
+ fourth_house_color=self.planets_settings[15]["color"],
508
+ c1=self.first_circle_radius,
509
+ c3=self.third_circle_radius,
510
+ chart_type=self.chart_type,
511
+ )
1343
512
 
1344
- # planets_color_X
1345
- for i in range(len(self.planets_settings)):
1346
- planet_id = self.planets_settings[i]["id"]
1347
- td[f"planets_color_{planet_id}"] = self.planets_settings[i]["color"]
513
+ # Draw planets
514
+ if self.chart_type in ["Transit", "Synastry"]:
515
+ template_dict["makePlanets"] = draw_planets(
516
+ available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
517
+ available_planets_setting=self.available_planets_setting,
518
+ second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
519
+ radius=self.main_radius,
520
+ main_subject_first_house_degree_ut=self.user.first_house.abs_pos,
521
+ main_subject_seventh_house_degree_ut=self.user.seventh_house.abs_pos,
522
+ chart_type=self.chart_type,
523
+ third_circle_radius=self.third_circle_radius,
524
+ )
525
+ else:
526
+ template_dict["makePlanets"] = draw_planets(
527
+ available_planets_setting=self.available_planets_setting,
528
+ chart_type=self.chart_type,
529
+ radius=self.main_radius,
530
+ available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
531
+ third_circle_radius=self.third_circle_radius,
532
+ main_subject_first_house_degree_ut=self.user.first_house.abs_pos,
533
+ main_subject_seventh_house_degree_ut=self.user.seventh_house.abs_pos
534
+ )
1348
535
 
1349
- # zodiac_color_X
1350
- for i in range(12):
1351
- td[f"zodiac_color_{i}"] = self.chart_colors_settings[f"zodiac_icon_{i}"]
1352
-
1353
- # orb_color_X
1354
- for i in range(len(self.aspects_settings)):
1355
- td[f"orb_color_{self.aspects_settings[i]['degree']}"] = self.aspects_settings[i]['color']
1356
-
1357
- # config
1358
- td["cfgZoom"] = str(self.zoom)
1359
- td["cfgRotate"] = rotate
1360
-
1361
- # ---
1362
- # Drawing Functions
1363
- #---
1364
-
1365
- td["makeZodiac"] = self._draw_zodiac_circle_slices(r)
1366
- td["makeHousesGrid"] = self._draw_house_grid()
1367
- # TODO: Add the rest of the functions
1368
- td["makeHouses"] = self._makeHouses(r)
1369
- td["makePlanets"] = self._make_planets(r)
1370
- td["elements_percentages"] = draw_elements_percentages(
536
+ # Draw elements percentages
537
+ template_dict["elements_percentages"] = draw_elements_percentages(
1371
538
  self.language_settings['fire'],
1372
539
  self.fire,
1373
540
  self.language_settings['earth'],
@@ -1377,29 +544,57 @@ class KerykeionChartSVG:
1377
544
  self.language_settings['water'],
1378
545
  self.water,
1379
546
  )
1380
- td["makePlanetGrid"] = self._makePlanetGrid()
1381
547
 
1382
- # Date time String
548
+ # Draw planet grid
549
+ if self.chart_type in ["Transit", "Synastry"]:
550
+ if self.chart_type == "Transit":
551
+ second_subject_table_name = self.language_settings["transit_name"]
552
+ else:
553
+ second_subject_table_name = self.t_user.name
554
+ template_dict["makePlanetGrid"] = draw_planet_grid(
555
+ planets_and_houses_grid_title=self.language_settings["planets_and_house"],
556
+ subject_name=self.user.name,
557
+ available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
558
+ chart_type=self.chart_type,
559
+ text_color=self.chart_colors_settings["paper_0"],
560
+ celestial_point_language=self.language_settings["celestial_points"],
561
+ second_subject_name=second_subject_table_name,
562
+ second_subject_available_kerykeion_celestial_points=self.t_available_kerykeion_celestial_points,
563
+ )
564
+ else:
565
+ template_dict["makePlanetGrid"] = draw_planet_grid(
566
+ planets_and_houses_grid_title=self.language_settings["planets_and_house"],
567
+ subject_name=self.user.name,
568
+ available_kerykeion_celestial_points=self.available_kerykeion_celestial_points,
569
+ chart_type=self.chart_type,
570
+ text_color=self.chart_colors_settings["paper_0"],
571
+ celestial_point_language=self.language_settings["celestial_points"],
572
+ )
573
+
574
+ # Set date time string
1383
575
  dt = datetime.fromisoformat(self.user.iso_formatted_local_datetime)
1384
- custom_format = dt.strftime('%Y-%-m-%-d %H:%M [%z]') # Note the use of '-' to remove leading zeros
576
+ custom_format = dt.strftime('%Y-%m-%d %H:%M [%z]')
1385
577
  custom_format = custom_format[:-3] + ':' + custom_format[-3:]
1386
- td["stringDateTime"] = f"{custom_format}"
578
+ template_dict["stringDateTime"] = f"{custom_format}"
1387
579
 
1388
- return td
580
+ return template_dict
1389
581
 
1390
582
  def makeTemplate(self, minify: bool = False) -> str:
1391
583
  """Creates the template for the SVG file"""
1392
- td = self._createTemplateDictionary()
584
+ td = self._create_template_dictionary()
1393
585
 
586
+ DATA_DIR = Path(__file__).parent
587
+ xml_svg = DATA_DIR / "templates" / "chart.xml"
588
+
1394
589
  # read template
1395
- with open(self.xml_svg, "r", encoding="utf-8", errors="ignore") as f:
590
+ with open(xml_svg, "r", encoding="utf-8", errors="ignore") as f:
1396
591
  template = Template(f.read()).substitute(td)
1397
592
 
1398
593
  # return filename
1399
594
 
1400
595
  logging.debug(f"Template dictionary keys: {td.keys()}")
1401
596
 
1402
- self._createTemplateDictionary()
597
+ self._create_template_dictionary()
1403
598
 
1404
599
  if minify:
1405
600
  template = scourString(template).replace('"', "'").replace("\n", "").replace("\t","").replace(" ", "").replace(" ", "")
@@ -1410,9 +605,9 @@ class KerykeionChartSVG:
1410
605
  return template
1411
606
 
1412
607
  def makeSVG(self, minify: bool = False):
1413
- """Prints out the SVG file in the specifide folder"""
608
+ """Prints out the SVG file in the specified folder"""
1414
609
 
1415
- if not (self.template):
610
+ if not hasattr(self, "template"):
1416
611
  self.template = self.makeTemplate(minify)
1417
612
 
1418
613
  self.chartname = self.output_directory / f"{self.user.name} - {self.chart_type} Chart.svg"
@@ -1422,6 +617,70 @@ class KerykeionChartSVG:
1422
617
 
1423
618
  logging.info(f"SVG Generated Correctly in: {self.chartname}")
1424
619
 
620
+ def makeWheelOnlyTemplate(self, minify: bool = False):
621
+ """Creates the template for the SVG file with only the wheel"""
622
+
623
+ with open(Path(__file__).parent / "templates" / "wheel_only.xml", "r", encoding="utf-8", errors="ignore") as f:
624
+ template = f.read()
625
+
626
+ template_dict = self._create_template_dictionary()
627
+ template = Template(template).substitute(template_dict)
628
+
629
+ if minify:
630
+ template = scourString(template).replace('"', "'").replace("\n", "").replace("\t","").replace(" ", "").replace(" ", "")
631
+
632
+ else:
633
+ template = template.replace('"', "'")
634
+
635
+ return template
636
+
637
+ def makeWheelOnlySVG(self, minify: bool = False):
638
+ """Prints out the SVG file in the specified folder with only the wheel"""
639
+
640
+ template = self.makeWheelOnlyTemplate(minify)
641
+
642
+ self.chartname = self.output_directory / f"{self.user.name} - {self.chart_type} Chart - Wheel Only.svg"
643
+
644
+ with open(self.chartname, "w", encoding="utf-8", errors="ignore") as output_file:
645
+ output_file.write(template)
646
+
647
+ logging.info(f"SVG Generated Correctly in: {self.chartname}")
648
+
649
+ def makeAspectGridOnlyTemplate(self, minify: bool = False):
650
+ """Creates the template for the SVG file with only the aspect grid"""
651
+
652
+ with open(Path(__file__).parent / "templates" / "aspect_grid_only.xml", "r", encoding="utf-8", errors="ignore") as f:
653
+ template = f.read()
654
+
655
+ template_dict = self._create_template_dictionary()
656
+
657
+ if self.chart_type in ["Transit", "Synastry"]:
658
+ aspects_grid = draw_transit_aspect_grid(self.chart_colors_settings['paper_0'], self.available_planets_setting, self.aspects_list)
659
+ else:
660
+ aspects_grid = draw_aspect_grid(self.chart_colors_settings['paper_0'], self.available_planets_setting, self.aspects_list, x_start=50, y_start=250)
661
+
662
+ template = Template(template).substitute({**template_dict, "makeAspectGrid": aspects_grid})
663
+
664
+ if minify:
665
+ template = scourString(template).replace('"', "'").replace("\n", "").replace("\t","").replace(" ", "").replace(" ", "")
666
+
667
+ else:
668
+ template = template.replace('"', "'")
669
+
670
+ return template
671
+
672
+ def makeAspectGridOnlySVG(self, minify: bool = False):
673
+ """Prints out the SVG file in the specified folder with only the aspect grid"""
674
+
675
+ template = self.makeAspectGridOnlyTemplate(minify)
676
+
677
+ self.chartname = self.output_directory / f"{self.user.name} - {self.chart_type} Chart - Aspect Grid Only.svg"
678
+
679
+ with open(self.chartname, "w", encoding="utf-8", errors="ignore") as output_file:
680
+ output_file.write(template)
681
+
682
+ logging.info(f"SVG Generated Correctly in: {self.chartname}")
683
+
1425
684
 
1426
685
  if __name__ == "__main__":
1427
686
  from kerykeion.utilities import setup_logging
@@ -1492,4 +751,139 @@ if __name__ == "__main__":
1492
751
  # With Topocentric Perspective
1493
752
  topocentric_subject = AstrologicalSubject("John Lennon - Topocentric", 1940, 10, 9, 18, 30, "Liverpool", "GB", perspective_type="Topocentric")
1494
753
  topocentric_chart = KerykeionChartSVG(topocentric_subject)
1495
- topocentric_chart.makeSVG()
754
+ topocentric_chart.makeSVG()
755
+
756
+ # Minified SVG
757
+ minified_subject = AstrologicalSubject("John Lennon - Minified", 1940, 10, 9, 18, 30, "Liverpool", "GB")
758
+ minified_chart = KerykeionChartSVG(minified_subject)
759
+ minified_chart.makeSVG(minify=True)
760
+
761
+ # Dark Theme Natal Chart
762
+ dark_theme_subject = AstrologicalSubject("John Lennon - Dark Theme", 1940, 10, 9, 18, 30, "Liverpool", "GB")
763
+ dark_theme_natal_chart = KerykeionChartSVG(dark_theme_subject, theme="dark")
764
+ dark_theme_natal_chart.makeSVG()
765
+
766
+ # Dark High Contrast Theme Natal Chart
767
+ dark_high_contrast_theme_subject = AstrologicalSubject("John Lennon - Dark High Contrast Theme", 1940, 10, 9, 18, 30, "Liverpool", "GB")
768
+ dark_high_contrast_theme_natal_chart = KerykeionChartSVG(dark_high_contrast_theme_subject, theme="dark-high-contrast")
769
+ dark_high_contrast_theme_natal_chart.makeSVG()
770
+
771
+ # Light Theme Natal Chart
772
+ light_theme_subject = AstrologicalSubject("John Lennon - Light Theme", 1940, 10, 9, 18, 30, "Liverpool", "GB")
773
+ light_theme_natal_chart = KerykeionChartSVG(light_theme_subject, theme="light")
774
+ light_theme_natal_chart.makeSVG()
775
+
776
+ # Dark Theme External Natal Chart
777
+ dark_theme_external_subject = AstrologicalSubject("John Lennon - Dark Theme External", 1940, 10, 9, 18, 30, "Liverpool", "GB")
778
+ dark_theme_external_chart = KerykeionChartSVG(dark_theme_external_subject, "ExternalNatal", second, theme="dark")
779
+ dark_theme_external_chart.makeSVG()
780
+
781
+ # Dark Theme Synastry Chart
782
+ dark_theme_synastry_subject = AstrologicalSubject("John Lennon - DTS", 1940, 10, 9, 18, 30, "Liverpool", "GB")
783
+ dark_theme_synastry_chart = KerykeionChartSVG(dark_theme_synastry_subject, "Synastry", second, theme="dark")
784
+ dark_theme_synastry_chart.makeSVG()
785
+
786
+ # Wheel Natal Only Chart
787
+ wheel_only_subject = AstrologicalSubject("John Lennon - Wheel Only", 1940, 10, 9, 18, 30, "Liverpool", "GB")
788
+ wheel_only_chart = KerykeionChartSVG(wheel_only_subject)
789
+ wheel_only_chart.makeWheelOnlySVG()
790
+
791
+ # Wheel External Natal Only Chart
792
+ wheel_external_subject = AstrologicalSubject("John Lennon - Wheel External Only", 1940, 10, 9, 18, 30, "Liverpool", "GB")
793
+ wheel_external_chart = KerykeionChartSVG(wheel_external_subject, "ExternalNatal", second)
794
+ wheel_external_chart.makeWheelOnlySVG()
795
+
796
+ # Wheel Synastry Only Chart
797
+ wheel_synastry_subject = AstrologicalSubject("John Lennon - Wheel Synastry Only", 1940, 10, 9, 18, 30, "Liverpool", "GB")
798
+ wheel_synastry_chart = KerykeionChartSVG(wheel_synastry_subject, "Synastry", second)
799
+ wheel_synastry_chart.makeWheelOnlySVG()
800
+
801
+ # Wheel Transit Only Chart
802
+ wheel_transit_subject = AstrologicalSubject("John Lennon - Wheel Transit Only", 1940, 10, 9, 18, 30, "Liverpool", "GB")
803
+ wheel_transit_chart = KerykeionChartSVG(wheel_transit_subject, "Transit", second)
804
+ wheel_transit_chart.makeWheelOnlySVG()
805
+
806
+ # Wheel Sidereal Birth Chart (Lahiri) Dark Theme
807
+ sidereal_dark_subject = AstrologicalSubject("John Lennon Lahiri - Dark Theme", 1940, 10, 9, 18, 30, "Liverpool", "GB", zodiac_type="Sidereal", sidereal_mode="LAHIRI")
808
+ sidereal_dark_chart = KerykeionChartSVG(sidereal_dark_subject, theme="dark")
809
+ sidereal_dark_chart.makeWheelOnlySVG()
810
+
811
+ # Wheel Sidereal Birth Chart (Fagan-Bradley) Light Theme
812
+ sidereal_light_subject = AstrologicalSubject("John Lennon Fagan-Bradley - Light Theme", 1940, 10, 9, 18, 30, "Liverpool", "GB", zodiac_type="Sidereal", sidereal_mode="FAGAN_BRADLEY")
813
+ sidereal_light_chart = KerykeionChartSVG(sidereal_light_subject, theme="light")
814
+ sidereal_light_chart.makeWheelOnlySVG()
815
+
816
+ # Aspect Grid Only Natal Chart
817
+ aspect_grid_only_subject = AstrologicalSubject("John Lennon - Aspect Grid Only", 1940, 10, 9, 18, 30, "Liverpool", "GB")
818
+ aspect_grid_only_chart = KerykeionChartSVG(aspect_grid_only_subject)
819
+ aspect_grid_only_chart.makeAspectGridOnlySVG()
820
+
821
+ # Aspect Grid Only Dark Theme Natal Chart
822
+ aspect_grid_dark_subject = AstrologicalSubject("John Lennon - Aspect Grid Dark Theme", 1940, 10, 9, 18, 30, "Liverpool", "GB")
823
+ aspect_grid_dark_chart = KerykeionChartSVG(aspect_grid_dark_subject, theme="dark")
824
+ aspect_grid_dark_chart.makeAspectGridOnlySVG()
825
+
826
+ # Aspect Grid Only Light Theme Natal Chart
827
+ aspect_grid_light_subject = AstrologicalSubject("John Lennon - Aspect Grid Light Theme", 1940, 10, 9, 18, 30, "Liverpool", "GB")
828
+ aspect_grid_light_chart = KerykeionChartSVG(aspect_grid_light_subject, theme="light")
829
+ aspect_grid_light_chart.makeAspectGridOnlySVG()
830
+
831
+ # Synastry Chart Aspect Grid Only
832
+ aspect_grid_synastry_subject = AstrologicalSubject("John Lennon - Aspect Grid Synastry", 1940, 10, 9, 18, 30, "Liverpool", "GB")
833
+ aspect_grid_synastry_chart = KerykeionChartSVG(aspect_grid_synastry_subject, "Synastry", second)
834
+ aspect_grid_synastry_chart.makeAspectGridOnlySVG()
835
+
836
+ # Transit Chart Aspect Grid Only
837
+ aspect_grid_transit_subject = AstrologicalSubject("John Lennon - Aspect Grid Transit", 1940, 10, 9, 18, 30, "Liverpool", "GB")
838
+ aspect_grid_transit_chart = KerykeionChartSVG(aspect_grid_transit_subject, "Transit", second)
839
+ aspect_grid_transit_chart.makeAspectGridOnlySVG()
840
+
841
+ # Synastry Chart Aspect Grid Only Dark Theme
842
+ aspect_grid_dark_synastry_subject = AstrologicalSubject("John Lennon - Aspect Grid Dark Synastry", 1940, 10, 9, 18, 30, "Liverpool", "GB")
843
+ aspect_grid_dark_synastry_chart = KerykeionChartSVG(aspect_grid_dark_synastry_subject, "Synastry", second, theme="dark")
844
+ aspect_grid_dark_synastry_chart.makeAspectGridOnlySVG()
845
+
846
+ # Transit Chart With draw_transit_aspect_grid table
847
+ transit_chart_with_table_grid_subject = AstrologicalSubject("John Lennon - TCWTG", 1940, 10, 9, 18, 30, "Liverpool", "GB")
848
+ transit_chart_with_table_grid = KerykeionChartSVG(transit_chart_with_table_grid_subject, "Transit", second, double_chart_aspect_grid_type="table", theme="dark")
849
+ transit_chart_with_table_grid.makeSVG()
850
+
851
+ # Chines Language Chart
852
+ chinese_subject = AstrologicalSubject("Hua Chenyu", 1990, 2, 7, 12, 0, "Hunan", "CN")
853
+ chinese_chart = KerykeionChartSVG(chinese_subject, chart_language="CN")
854
+ chinese_chart.makeSVG()
855
+
856
+ # French Language Chart
857
+ french_subject = AstrologicalSubject("Jeanne Moreau", 1928, 1, 23, 10, 0, "Paris", "FR")
858
+ french_chart = KerykeionChartSVG(french_subject, chart_language="FR")
859
+ french_chart.makeSVG()
860
+
861
+ # Spanish Language Chart
862
+ spanish_subject = AstrologicalSubject("Antonio Banderas", 1960, 8, 10, 12, 0, "Malaga", "ES")
863
+ spanish_chart = KerykeionChartSVG(spanish_subject, chart_language="ES")
864
+ spanish_chart.makeSVG()
865
+
866
+ # Portuguese Language Chart
867
+ portuguese_subject = AstrologicalSubject("Cristiano Ronaldo", 1985, 2, 5, 5, 25, "Funchal", "PT")
868
+ portuguese_chart = KerykeionChartSVG(portuguese_subject, chart_language="PT")
869
+ portuguese_chart.makeSVG()
870
+
871
+ # Italian Language Chart
872
+ italian_subject = AstrologicalSubject("Sophia Loren", 1934, 9, 20, 2, 0, "Rome", "IT")
873
+ italian_chart = KerykeionChartSVG(italian_subject, chart_language="IT")
874
+ italian_chart.makeSVG()
875
+
876
+ # Russian Language Chart
877
+ russian_subject = AstrologicalSubject("Mikhail Bulgakov", 1891, 5, 15, 12, 0, "Kiev", "UA")
878
+ russian_chart = KerykeionChartSVG(russian_subject, chart_language="RU")
879
+ russian_chart.makeSVG()
880
+
881
+ # Turkish Language Chart
882
+ turkish_subject = AstrologicalSubject("Mehmet Oz", 1960, 6, 11, 12, 0, "Istanbul", "TR")
883
+ turkish_chart = KerykeionChartSVG(turkish_subject, chart_language="TR")
884
+ turkish_chart.makeSVG()
885
+
886
+ # German Language Chart
887
+ german_subject = AstrologicalSubject("Albert Einstein", 1879, 3, 14, 11, 30, "Ulm", "DE")
888
+ german_chart = KerykeionChartSVG(german_subject, chart_language="DE")
889
+ german_chart.makeSVG()