kerykeion 4.6.1__py3-none-any.whl → 4.7.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.

@@ -4,20 +4,32 @@
4
4
  """
5
5
 
6
6
 
7
- import pytz
8
7
  import logging
9
8
 
10
- from datetime import datetime
11
9
  from kerykeion.settings.kerykeion_settings import get_settings
12
10
  from kerykeion.aspects.synastry_aspects import SynastryAspects
13
11
  from kerykeion.aspects.natal_aspects import NatalAspects
14
12
  from kerykeion.astrological_subject import AstrologicalSubject
15
13
  from kerykeion.kr_types import KerykeionException, ChartType
16
14
  from kerykeion.kr_types import ChartTemplateDictionary
17
- from kerykeion.charts.charts_utils import decHourJoin, degreeDiff, offsetToTz, sliceToX, sliceToY, draw_zodiac_slice
15
+ from kerykeion.kr_types.settings_models import KerykeionSettingsCelestialPointModel
16
+ from kerykeion.charts.charts_utils import (
17
+ degreeDiff,
18
+ sliceToX,
19
+ sliceToY,
20
+ draw_zodiac_slice,
21
+ convert_latitude_coordinate_to_string,
22
+ convert_longitude_coordinate_to_string,
23
+ draw_aspect_line,
24
+ draw_elements_percentages,
25
+ convert_decimal_to_degree_string,
26
+ draw_transit_ring_degree_steps,
27
+ draw_degree_ring,
28
+ draw_transit_ring
29
+ )
18
30
  from pathlib import Path
19
31
  from string import Template
20
- from typing import Union
32
+ from typing import Union, List
21
33
 
22
34
 
23
35
  class KerykeionChartSVG:
@@ -34,6 +46,7 @@ class KerykeionChartSVG:
34
46
  - new_settings_file: Set the settings file (default: kr.config.json)
35
47
  """
36
48
 
49
+ # Set at init
37
50
  first_obj: AstrologicalSubject
38
51
  second_obj: Union[AstrologicalSubject, None]
39
52
  chart_type: ChartType
@@ -41,6 +54,46 @@ class KerykeionChartSVG:
41
54
  new_settings_file: Union[Path, None]
42
55
  output_directory: Path
43
56
 
57
+ # Internal properties
58
+ fire: float
59
+ earth: float
60
+ air: float
61
+ water: float
62
+ c1: float
63
+ c2: float
64
+ c3: float
65
+ homedir: Path
66
+ xml_svg: Path
67
+ natal_width: float
68
+ full_width: float
69
+ language_settings: dict
70
+ chart_colors_settings: dict
71
+ planets_settings: dict
72
+ aspects_settings: dict
73
+ planet_in_zodiac_extra_points: int
74
+ chart_settings: dict
75
+ user: AstrologicalSubject
76
+ available_planets_setting: List[KerykeionSettingsCelestialPointModel]
77
+ transit_ring_exclude_points_names: List[str]
78
+ points_deg_ut: list
79
+ points_deg: list
80
+ points_sign: list
81
+ points_retrograde: list
82
+ houses_sign_graph: list
83
+ t_points_deg_ut: list
84
+ t_points_deg: list
85
+ t_points_sign: list
86
+ t_points_retrograde: list
87
+ t_houses_sign_graph: list
88
+ screen_width: float
89
+ screen_height: float
90
+ location: str
91
+ geolat: float
92
+ geolon: float
93
+ zoom: int
94
+ zodiac: tuple
95
+ template: str
96
+
44
97
  def __init__(
45
98
  self,
46
99
  first_obj: AstrologicalSubject,
@@ -77,7 +130,23 @@ class KerykeionChartSVG:
77
130
  continue
78
131
 
79
132
  self.available_planets_setting.append(body)
80
-
133
+
134
+ # House cusp points are excluded from the transit ring.
135
+ self.transit_ring_exclude_points_names = [
136
+ "First_House",
137
+ "Second_House",
138
+ "Third_House",
139
+ "Fourth_House",
140
+ "Fifth_House",
141
+ "Sixth_House",
142
+ "Seventh_House",
143
+ "Eighth_House",
144
+ "Ninth_House",
145
+ "Tenth_House",
146
+ "Eleventh_House",
147
+ "Twelfth_House"
148
+ ]
149
+
81
150
  # Available bodies
82
151
  available_celestial_points = []
83
152
  for body in self.available_planets_setting:
@@ -152,57 +221,15 @@ class KerykeionChartSVG:
152
221
  self.screen_width = 1200
153
222
  self.screen_height = 772.2
154
223
 
155
- # check for home
156
- self.home_location = self.user.city
157
- self.home_geolat = self.user.lat
158
- self.home_geolon = self.user.lng
159
- self.home_countrycode = self.user.nation
160
- self.home_timezonestr = self.user.tz_str
161
-
162
- logging.info(f"{self.user.name} birth location: {self.home_location}, {self.home_geolat}, {self.home_geolon}")
163
-
164
224
  # default location
165
- self.location = self.home_location
166
- self.geolat = float(self.home_geolat)
167
- self.geolon = float(self.home_geolon)
168
- self.countrycode = self.home_countrycode
169
- self.timezonestr = self.home_timezonestr
170
-
171
- # current datetime
172
- now = datetime.now()
173
-
174
- # aware datetime object
175
- dt_input = datetime(now.year, now.month, now.day, now.hour, now.minute, now.second)
176
- dt = pytz.timezone(self.timezonestr).localize(dt_input)
177
-
178
- # naive utc datetime object
179
- dt_utc = dt.replace(tzinfo=None) - dt.utcoffset() # type: ignore
180
-
181
- # Default
182
- self.name = self.user.name
183
- self.charttype = self.chart_type
184
- self.year = self.user.utc.year
185
- self.month = self.user.utc.month
186
- self.day = self.user.utc.day
187
- self.hour = self.user.utc.hour + self.user.utc.minute / 100
188
- self.timezone = offsetToTz(dt.utcoffset())
189
- self.altitude = 25
190
- self.geonameid = None
191
-
192
- # Transit
225
+ self.location = self.user.city
226
+ self.geolat = self.user.lat
227
+ self.geolon = self.user.lng
228
+
229
+ logging.info(f"{self.user.name} birth location: {self.location}, {self.geolat}, {self.geolon}")
193
230
 
194
231
  if self.chart_type == "Transit":
195
- self.t_geolon = self.geolon
196
- self.t_geolat = self.geolat
197
- self.t_altitude = self.altitude
198
232
  self.t_name = self.language_settings["transit_name"]
199
- self.t_year = dt_utc.year
200
- self.t_month = dt_utc.month
201
- self.t_day = dt_utc.day
202
- self.t_hour = decHourJoin(dt_utc.hour, dt_utc.minute, dt_utc.second)
203
- self.t_timezone = offsetToTz(dt.utcoffset())
204
- self.t_altitude = 25
205
- self.t_geonameid = None
206
233
 
207
234
  # configuration
208
235
  # ZOOM 1 = 100%
@@ -247,130 +274,6 @@ class KerykeionChartSVG:
247
274
  self.planet_in_zodiac_extra_points = settings["general_settings"]["planet_in_zodiac_extra_points"]
248
275
  self.chart_settings = settings["chart_settings"]
249
276
 
250
- def _transitRing(self, r) -> str:
251
- """
252
- Draws the transit ring.
253
- """
254
- radius_offset = 18
255
-
256
- out = f'<circle cx="{r}" cy="{r}" r="{r - radius_offset}" style="fill: none; stroke: {self.chart_colors_settings["paper_1"]}; stroke-width: 36px; stroke-opacity: .4;"/>'
257
- out += f'<circle cx="{r}" cy="{r}" r="{r}" style="fill: none; stroke: {self.chart_colors_settings["zodiac_transit_ring_3"]}; stroke-width: 1px; stroke-opacity: .6;"/>'
258
-
259
- return out
260
-
261
- def _degreeRing(self, r) -> str:
262
- """
263
- Draws the degree ring.
264
- """
265
- out = ""
266
- for i in range(72):
267
- offset = float(i * 5) - self.user.houses_degree_ut[6]
268
- if offset < 0:
269
- offset = offset + 360.0
270
- elif offset > 360:
271
- offset = offset - 360.0
272
- x1 = sliceToX(0, r - self.c1, offset) + self.c1
273
- y1 = sliceToY(0, r - self.c1, offset) + self.c1
274
- x2 = sliceToX(0, r + 2 - self.c1, offset) - 2 + self.c1
275
- y2 = sliceToY(0, r + 2 - self.c1, offset) - 2 + self.c1
276
-
277
- out += f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke: {self.chart_colors_settings["paper_0"]}; stroke-width: 1px; stroke-opacity:.9;"/>'
278
-
279
- return out
280
-
281
- def _degreeTransitRing(self, r):
282
- out = ""
283
- for i in range(72):
284
- offset = float(i * 5) - self.user.houses_degree_ut[6]
285
- if offset < 0:
286
- offset = offset + 360.0
287
- elif offset > 360:
288
- offset = offset - 360.0
289
- x1 = sliceToX(0, r, offset)
290
- y1 = sliceToY(0, r, offset)
291
- x2 = sliceToX(0, r + 2, offset) - 2
292
- y2 = sliceToY(0, r + 2, offset) - 2
293
- out += f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke: #F00; stroke-width: 1px; stroke-opacity:.9;"/>'
294
-
295
- return out
296
-
297
- def _lat2str(self, coord):
298
- """Converts a floating point latitude to string with
299
- degree, minutes and seconds and the appropriate sign
300
- (north or south). Eg. 52.1234567 -> 52°7'25" N
301
-
302
- Args:
303
- coord (float): latitude in floating point format
304
- Returns:
305
- str: latitude in string format with degree, minutes,
306
- seconds and sign (N/S)
307
- """
308
-
309
- sign = self.language_settings["north"]
310
- if coord < 0.0:
311
- sign = self.language_settings["south"]
312
- coord = abs(coord)
313
- deg = int(coord)
314
- min = int((float(coord) - deg) * 60)
315
- sec = int(round(float(((float(coord) - deg) * 60) - min) * 60.0))
316
- return f"{deg}°{min}'{sec}\" {sign}"
317
-
318
- def _lon2str(self, coord):
319
- """Converts a floating point longitude to string with
320
- degree, minutes and seconds and the appropriate sign
321
- (east or west). Eg. 52.1234567 -> 52°7'25" E
322
-
323
- Args:
324
- coord (float): longitude in floating point format
325
- Returns:
326
- str: longitude in string format with degree, minutes,
327
- seconds and sign (E/W)
328
- """
329
-
330
- sign = self.language_settings["east"]
331
- if coord < 0.0:
332
- sign = self.language_settings["west"]
333
- coord = abs(coord)
334
- deg = int(coord)
335
- min = int((float(coord) - deg) * 60)
336
- sec = int(round(float(((float(coord) - deg) * 60) - min) * 60.0))
337
- return f"{deg}°{min}'{sec}\" {sign}"
338
-
339
- def _dec2deg(self, dec, type="3"):
340
- """Coverts decimal float to degrees in format
341
- a°b'c".
342
- """
343
-
344
- dec = float(dec)
345
- a = int(dec)
346
- a_new = (dec - float(a)) * 60.0
347
- b_rounded = int(round(a_new))
348
- b = int(a_new)
349
- c = int(round((a_new - float(b)) * 60.0))
350
- if type == "3":
351
- out = f"{a:02d}&#176;{b:02d}&#39;{c:02d}&#34;"
352
- elif type == "2":
353
- out = f"{a:02d}&#176;{b_rounded:02d}&#39;"
354
- elif type == "1":
355
- out = f"{a:02d}&#176;"
356
- else:
357
- raise KerykeionException(f"Wrong type: {type}, it must be 1, 2 or 3.")
358
- return str(out)
359
-
360
- def _drawAspect(self, r, ar, degA, degB, color):
361
- """
362
- Draws svg aspects: ring, aspect ring, degreeA degreeB
363
- """
364
- offset = (int(self.user.houses_degree_ut[6]) / -1) + int(degA)
365
- x1 = sliceToX(0, ar, offset) + (r - ar)
366
- y1 = sliceToY(0, ar, offset) + (r - ar)
367
- offset = (int(self.user.houses_degree_ut[6]) / -1) + int(degB)
368
- x2 = sliceToX(0, ar, offset) + (r - ar)
369
- y2 = sliceToY(0, ar, offset) + (r - ar)
370
- out = f' <line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" style="stroke: {color}; stroke-width: 1; stroke-opacity: .9;"/>'
371
-
372
- return out
373
-
374
277
  def _draw_zodiac_circle_slices(self, r):
375
278
  """
376
279
  Generate the SVG string representing the zodiac circle
@@ -388,7 +291,7 @@ class KerykeionChartSVG:
388
291
  output += draw_zodiac_slice(
389
292
  c1=self.c1,
390
293
  chart_type=self.chart_type,
391
- sixth_house_degree_ut=self.user.houses_degree_ut[6],
294
+ seventh_house_degree_ut=self.user.houses_degree_ut[6],
392
295
  num=i,
393
296
  r=r,
394
297
  style=f'fill:{self.chart_colors_settings[f"zodiac_bg_{i}"]}; fill-opacity: 0.5;',
@@ -484,44 +387,50 @@ class KerykeionChartSVG:
484
387
 
485
388
  return path
486
389
 
487
- def _value_element_from_planet(self, i):
390
+ def _calculate_elements_points_from_planets(self):
488
391
  """
489
392
  Calculate chart element points from a planet.
490
393
  """
394
+
395
+ for i in range(len(self.available_planets_setting)):
396
+ # element: get extra points if planet is in own zodiac sign.
397
+ related_zodiac_signs = self.available_planets_setting[i]["related_zodiac_signs"]
398
+ cz = self.points_sign[i]
399
+ extra_points = 0
400
+ if related_zodiac_signs != []:
401
+ for e in range(len(related_zodiac_signs)):
402
+ if int(related_zodiac_signs[e]) == int(cz):
403
+ extra_points = self.planet_in_zodiac_extra_points
491
404
 
492
- # element: get extra points if planet is in own zodiac sign.
493
- related_zodiac_signs = self.available_planets_setting[i]["related_zodiac_signs"]
494
- cz = self.points_sign[i]
495
- extra_points = 0
496
- if related_zodiac_signs != []:
497
- for e in range(len(related_zodiac_signs)):
498
- if int(related_zodiac_signs[e]) == int(cz):
499
- extra_points = self.planet_in_zodiac_extra_points
500
-
501
- ele = self.zodiac[self.points_sign[i]]["element"]
502
- if ele == "fire":
503
- self.fire = self.fire + self.available_planets_setting[i]["element_points"] + extra_points
405
+ ele = self.zodiac[self.points_sign[i]]["element"]
406
+ if ele == "fire":
407
+ self.fire = self.fire + self.available_planets_setting[i]["element_points"] + extra_points
504
408
 
505
- elif ele == "earth":
506
- self.earth = self.earth + self.available_planets_setting[i]["element_points"] + extra_points
409
+ elif ele == "earth":
410
+ self.earth = self.earth + self.available_planets_setting[i]["element_points"] + extra_points
507
411
 
508
- elif ele == "air":
509
- self.air = self.air + self.available_planets_setting[i]["element_points"] + extra_points
412
+ elif ele == "air":
413
+ self.air = self.air + self.available_planets_setting[i]["element_points"] + extra_points
510
414
 
511
- elif ele == "water":
512
- self.water = self.water + self.available_planets_setting[i]["element_points"] + extra_points
415
+ elif ele == "water":
416
+ self.water = self.water + self.available_planets_setting[i]["element_points"] + extra_points
513
417
 
514
418
  def _make_planets(self, r):
515
419
  planets_degut = {}
516
420
  diff = range(len(self.available_planets_setting))
517
421
 
518
422
  for i in range(len(self.available_planets_setting)):
519
- if self.available_planets_setting[i]["is_active"] == 1:
520
- # list of planets sorted by degree
521
- logging.debug(f"planet: {i}, degree: {self.points_deg_ut[i]}")
522
- planets_degut[self.points_deg_ut[i]] = i
423
+ # list of planets sorted by degree
424
+ logging.debug(f"planet: {i}, degree: {self.points_deg_ut[i]}")
425
+ planets_degut[self.points_deg_ut[i]] = i
523
426
 
524
- self._value_element_from_planet(i)
427
+ """
428
+ FIXME: The planets_degut is a dictionary like:
429
+ {planet_degree: planet_index}
430
+ It should be replaced bu points_deg_ut
431
+ print(self.points_deg_ut)
432
+ print(planets_degut)
433
+ """
525
434
 
526
435
  output = ""
527
436
  keys = list(planets_degut.keys())
@@ -703,14 +612,15 @@ class KerykeionChartSVG:
703
612
  if self.chart_type == "Transit" or self.chart_type == "Synastry":
704
613
  group_offset = {}
705
614
  t_planets_degut = {}
706
- if self.chart_type == "Transit":
707
- list_range = len(self.available_planets_setting) - 4
708
- else:
709
- list_range = len(self.available_planets_setting)
615
+ list_range = len(self.available_planets_setting)
616
+
710
617
  for i in range(list_range):
618
+ if self.chart_type == "Transit" and self.available_planets_setting[i]['name'] in self.transit_ring_exclude_points_names:
619
+ continue
620
+
711
621
  group_offset[i] = 0
712
- if self.available_planets_setting[i]["is_active"] == 1:
713
- t_planets_degut[self.t_points_deg_ut[i]] = i
622
+ t_planets_degut[self.t_points_deg_ut[i]] = i
623
+
714
624
  t_keys = list(t_planets_degut.keys())
715
625
  t_keys.sort()
716
626
 
@@ -752,7 +662,12 @@ class KerykeionChartSVG:
752
662
  group_offset[groups[i][3]] = 2.0
753
663
 
754
664
  switch = 0
665
+
666
+ # Transit planets loop
755
667
  for e in range(len(t_keys)):
668
+ if self.chart_type == "Transit" and self.available_planets_setting[e]["name"] in self.transit_ring_exclude_points_names:
669
+ continue
670
+
756
671
  i = t_planets_degut[t_keys[e]]
757
672
 
758
673
  if 22 < i < 27:
@@ -764,20 +679,21 @@ class KerykeionChartSVG:
764
679
  rplanet = 26
765
680
  switch = 1
766
681
 
682
+ # Transit planet name
767
683
  zeropoint = 360 - self.user.houses_degree_ut[6]
768
684
  t_offset = zeropoint + self.t_points_deg_ut[i]
769
685
  if t_offset > 360:
770
686
  t_offset = t_offset - 360
771
687
  planet_x = sliceToX(0, (r - rplanet), t_offset) + rplanet
772
688
  planet_y = sliceToY(0, (r - rplanet), t_offset) + rplanet
773
- output += f'<g 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>'
689
+ 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>'
774
690
 
775
- # transit planet line
691
+ # Transit planet line
776
692
  x1 = sliceToX(0, r + 3, t_offset) - 3
777
693
  y1 = sliceToY(0, r + 3, t_offset) - 3
778
694
  x2 = sliceToX(0, r - 3, t_offset) + 3
779
695
  y2 = sliceToY(0, r - 3, t_offset) + 3
780
- output += f'<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;"/>'
696
+ 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;"/>'
781
697
 
782
698
  # transit planet degree text
783
699
  rotate = self.user.houses_degree_ut[0] - self.t_points_deg_ut[i]
@@ -801,7 +717,7 @@ class KerykeionChartSVG:
801
717
  degree = int(t_offset)
802
718
  output += f'<g transform="translate({deg_x},{deg_y})">'
803
719
  output += f'<text transform="rotate({rotate})" text-anchor="{textanchor}'
804
- output += f'" style="fill: {self.available_planets_setting[i]["color"]}; font-size: 10px;">{self._dec2deg(self.t_points_deg[i], type="1")}'
720
+ 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")}'
805
721
  output += "</text></g>"
806
722
 
807
723
  # check transit
@@ -983,12 +899,13 @@ class KerykeionChartSVG:
983
899
  def _makeAspects(self, r, ar):
984
900
  out = ""
985
901
  for element in self.aspects_list:
986
- out += self._drawAspect(
987
- r,
988
- ar,
989
- element["p1_abs_pos"],
990
- element["p2_abs_pos"],
991
- self.aspects_settings[element["aid"]]["color"],
902
+ out += draw_aspect_line(
903
+ r=r,
904
+ ar=ar,
905
+ degA=element["p1_abs_pos"],
906
+ degB=element["p2_abs_pos"],
907
+ color=self.aspects_settings[element["aid"]]["color"],
908
+ seventh_house_degree_ut=self.user.seventh_house.abs_pos
992
909
  )
993
910
 
994
911
  return out
@@ -1004,24 +921,22 @@ class KerykeionChartSVG:
1004
921
  counter = 0
1005
922
  for a in revr:
1006
923
  counter += 1
1007
- if self.available_planets_setting[a]["is_active"] == 1:
1008
- out += f'<rect x="{xindent}" y="{yindent}" width="{box}" height="{box}" style="{style}"/>'
1009
- 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"]}" />'
1010
-
1011
- xindent = xindent + box
1012
- yindent = yindent - box
1013
- revr2 = list(range(a))
1014
- revr2.reverse()
1015
- xorb = xindent
1016
- yorb = yindent + box
1017
- for b in revr2:
1018
- if self.available_planets_setting[b]["is_active"] == 1:
1019
- out += f'<rect x="{xorb}" y="{yorb}" width="{box}" height="{box}" style="{style}"/>'
1020
-
1021
- xorb = xorb + box
1022
- for element in self.aspects_list:
1023
- if (element["p1"] == a and element["p2"] == b) or (element["p1"] == b and element["p2"] == a):
1024
- out += f'<use x="{xorb-box+1}" y="{yorb+1}" xlink:href="#orb{element["aspect_degrees"]}" />'
924
+ out += f'<rect x="{xindent}" y="{yindent}" width="{box}" height="{box}" style="{style}"/>'
925
+ 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"]}" />'
926
+
927
+ xindent = xindent + box
928
+ yindent = yindent - box
929
+ revr2 = list(range(a))
930
+ revr2.reverse()
931
+ xorb = xindent
932
+ yorb = yindent + box
933
+ for b in revr2:
934
+ out += f'<rect x="{xorb}" y="{yorb}" width="{box}" height="{box}" style="{style}"/>'
935
+
936
+ xorb = xorb + box
937
+ for element in self.aspects_list:
938
+ if (element["p1"] == a and element["p2"] == b) or (element["p1"] == b and element["p2"] == a):
939
+ out += f'<use x="{xorb-box+1}" y="{yorb+1}" xlink:href="#orb{element["aspect_degrees"]}" />'
1025
940
 
1026
941
  return out
1027
942
 
@@ -1032,12 +947,13 @@ class KerykeionChartSVG:
1032
947
  self.aspects_list = SynastryAspects(self.user, self.t_user, new_settings_file=self.new_settings_file).relevant_aspects
1033
948
 
1034
949
  for element in self.aspects_list:
1035
- out += self._drawAspect(
1036
- r,
1037
- ar,
1038
- element["p1_abs_pos"],
1039
- element["p2_abs_pos"],
1040
- self.aspects_settings[element["aid"]]["color"],
950
+ out += draw_aspect_line(
951
+ r=r,
952
+ ar=ar,
953
+ degA=element["p1_abs_pos"],
954
+ degB=element["p2_abs_pos"],
955
+ color=self.aspects_settings[element["aid"]]["color"],
956
+ seventh_house_degree_ut=self.user.seventh_house.abs_pos
1041
957
  )
1042
958
 
1043
959
  return out
@@ -1088,36 +1004,20 @@ class KerykeionChartSVG:
1088
1004
 
1089
1005
  out += "</g>"
1090
1006
  # difference in degrees
1091
- out += f'<text y="8" x="45" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{self._dec2deg(self.aspects_list[i]["orbit"])}</text>'
1007
+ 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>'
1092
1008
  # line
1093
1009
  out += "</g>"
1094
1010
  line = line + 14
1095
1011
  out += "</g>"
1096
1012
  return out
1097
1013
 
1098
- def _makeElements(self, r):
1099
- total = self.fire + self.earth + self.air + self.water
1100
- pf = int(round(100 * self.fire / total))
1101
- pe = int(round(100 * self.earth / total))
1102
- pa = int(round(100 * self.air / total))
1103
- pw = int(round(100 * self.water / total))
1104
-
1105
- out = '<g transform="translate(-30,79)">'
1106
- out += f'<text y="0" style="fill:#ff6600; font-size: 10px;">{self.language_settings["fire"]} {str(pf)}%</text>'
1107
- out += f'<text y="12" style="fill:#6a2d04; font-size: 10px;">{self.language_settings["earth"]} {str(pe)}%</text>'
1108
- out += f'<text y="24" style="fill:#6f76d1; font-size: 10px;">{self.language_settings["air"]} {str(pa)}%</text>'
1109
- out += f'<text y="36" style="fill:#630e73; font-size: 10px;">{self.language_settings["water"]} {str(pw)}%</text>'
1110
- out += "</g>"
1111
-
1112
- return out
1113
-
1114
1014
  def _makePlanetGrid(self):
1115
1015
  li = 10
1116
1016
  offset = 0
1117
1017
 
1118
1018
  out = '<g transform="translate(500,-20)">'
1119
1019
  out += '<g transform="translate(140, -15)">'
1120
- out += f'<text text-anchor="end" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 14px;">{self.language_settings["planets_and_house"]} {self.name}:</text>'
1020
+ 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>'
1121
1021
  out += "</g>"
1122
1022
 
1123
1023
  end_of_line = None
@@ -1140,7 +1040,7 @@ class KerykeionChartSVG:
1140
1040
  out += f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g>'
1141
1041
 
1142
1042
  # planet degree
1143
- out += f'<text text-anchor="start" x="19" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{self._dec2deg(self.points_deg[i])}</text>'
1043
+ 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>'
1144
1044
 
1145
1045
  # zodiac
1146
1046
  out += f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.points_sign[i]]["name"]}" /></g>'
@@ -1172,27 +1072,26 @@ class KerykeionChartSVG:
1172
1072
  t_li = 10
1173
1073
  t_offset = -120
1174
1074
 
1175
- if self.available_planets_setting[i]["is_active"] == 1:
1176
- # start of line
1177
- out += f'<g transform="translate({t_offset},{t_li})">'
1075
+ # start of line
1076
+ out += f'<g transform="translate({t_offset},{t_li})">'
1178
1077
 
1179
- # planet text
1180
- 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>'
1181
- # planet symbol
1182
- out += f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g>'
1183
- # planet degree
1184
- out += f'<text text-anchor="start" x="19" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{self._dec2deg(self.t_points_deg[i])}</text>'
1185
- # zodiac
1186
- out += f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.t_points_sign[i]]["name"]}" /></g>'
1078
+ # planet text
1079
+ 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>'
1080
+ # planet symbol
1081
+ out += f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g>'
1082
+ # planet degree
1083
+ 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>'
1084
+ # zodiac
1085
+ out += f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.t_points_sign[i]]["name"]}" /></g>'
1187
1086
 
1188
- # planet retrograde
1189
- if self.t_points_retrograde[i]:
1190
- out += '<g transform="translate(74,-6)"><use transform="scale(.5)" xlink:href="#retrograde" /></g>'
1087
+ # planet retrograde
1088
+ if self.t_points_retrograde[i]:
1089
+ out += '<g transform="translate(74,-6)"><use transform="scale(.5)" xlink:href="#retrograde" /></g>'
1191
1090
 
1192
- # end of line
1193
- out += end_of_line
1091
+ # end of line
1092
+ out += end_of_line
1194
1093
 
1195
- t_li = t_li + offset_between_lines
1094
+ t_li = t_li + offset_between_lines
1196
1095
 
1197
1096
  if end_of_line is None:
1198
1097
  raise KerykeionException("End of line not found")
@@ -1200,48 +1099,47 @@ class KerykeionChartSVG:
1200
1099
  out += end_of_line
1201
1100
  return out
1202
1101
 
1203
- def _makeHousesGrid(self):
1102
+ def _draw_house_grid(self):
1204
1103
  """
1205
1104
  Generate SVG code for a grid of astrological houses.
1206
1105
 
1207
1106
  Returns:
1208
1107
  str: The SVG code for the grid of houses.
1209
1108
  """
1210
- # Initialize the SVG group for the grid
1211
- grid_svg = '<g transform="translate(600,-20)">'
1212
-
1213
- # Vertical position of the current house in the grid
1214
- vertical_position = 10
1109
+ out = '<g transform="translate(600,-20)">'
1215
1110
 
1216
- # Generate SVG code for each house
1111
+ li = 10
1217
1112
  for i in range(12):
1218
- # Add leading spaces to single-digit house numbers for alignment
1219
- house_number = str(i + 1).rjust(2, ' ')
1220
-
1221
- # Start a new SVG group for the current house
1222
- grid_svg += f'<g transform="translate(0,{vertical_position})">'
1223
-
1224
- # Add the house label
1225
- grid_svg += f'<text text-anchor="end" x="40" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;">{self.language_settings["cusp"]} {house_number}:</text>'
1226
-
1227
- # Add the zodiac sign symbol for the house
1228
- zodiac_sign = self.zodiac[self.houses_sign_graph[i]]["name"]
1229
- grid_svg += f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{zodiac_sign}" /></g>'
1230
-
1231
- # Add the position of the house
1232
- house_position = self._dec2deg(self.user.houses_list[i]["position"])
1233
- grid_svg += f'<text x="53" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;"> {house_position}</text>'
1234
-
1235
- # End the SVG group for the current house
1236
- grid_svg += "</g>"
1113
+ if i < 9:
1114
+ cusp = "&#160;&#160;" + str(i + 1)
1115
+ else:
1116
+ cusp = str(i + 1)
1117
+ out += f'<g transform="translate(0,{li})">'
1118
+ 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>'
1119
+ out += f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.houses_sign_graph[i]]["name"]}" /></g>'
1120
+ 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>'
1121
+ out += "</g>"
1122
+ li = li + 14
1237
1123
 
1238
- # Move down for the next house
1239
- vertical_position += 14
1124
+ out += "</g>"
1240
1125
 
1241
- # End the SVG group for the grid
1242
- grid_svg += "</g>"
1126
+ if self.chart_type == "Synastry":
1127
+ out += '<g transform="translate(840, -20)">'
1128
+ li = 10
1129
+ for i in range(12):
1130
+ if i < 9:
1131
+ cusp = "&#160;&#160;" + str(i + 1)
1132
+ else:
1133
+ cusp = str(i + 1)
1134
+ out += '<g transform="translate(0,' + str(li) + ')">'
1135
+ 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>'
1136
+ out += f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.t_houses_sign_graph[i]]["name"]}" /></g>'
1137
+ 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>'
1138
+ out += "</g>"
1139
+ li = li + 14
1140
+ out += "</g>"
1243
1141
 
1244
- return grid_svg
1142
+ return out
1245
1143
 
1246
1144
  def _createTemplateDictionary(self) -> ChartTemplateDictionary:
1247
1145
  # self.chart_type = "Transit"
@@ -1251,6 +1149,9 @@ class KerykeionChartSVG:
1251
1149
  self.air = 0.0
1252
1150
  self.water = 0.0
1253
1151
 
1152
+ # Calculate the elements points
1153
+ self._calculate_elements_points_from_planets()
1154
+
1254
1155
  # width and height from screen
1255
1156
  ratio = float(self.screen_width) / float(self.screen_height)
1256
1157
  if ratio < 1.3: # 1280x1024
@@ -1285,8 +1186,8 @@ class KerykeionChartSVG:
1285
1186
 
1286
1187
  # transit
1287
1188
  if self.chart_type == "Transit" or self.chart_type == "Synastry":
1288
- td["transitRing"] = self._transitRing(r)
1289
- td["degreeRing"] = self._degreeTransitRing(r)
1189
+ td["transitRing"] = draw_transit_ring(r, self.chart_colors_settings["paper_1"], self.chart_colors_settings["zodiac_transit_ring_3"])
1190
+ td["degreeRing"] = draw_transit_ring_degree_steps(r, self.user.seventh_house.abs_pos)
1290
1191
 
1291
1192
  # circles
1292
1193
  td["c1"] = f'cx="{r}" cy="{r}" r="{r - 36}"'
@@ -1303,7 +1204,7 @@ class KerykeionChartSVG:
1303
1204
  td["chart_width"] = self.full_width
1304
1205
  else:
1305
1206
  td["transitRing"] = ""
1306
- td["degreeRing"] = self._degreeRing(r)
1207
+ td["degreeRing"] = draw_degree_ring(r, self.c1, self.user.seventh_house.abs_pos, self.chart_colors_settings["paper_0"])
1307
1208
 
1308
1209
  # circles
1309
1210
  td["c1"] = f'cx="{r}" cy="{r}" r="{r - self.c1}"'
@@ -1324,17 +1225,17 @@ class KerykeionChartSVG:
1324
1225
  td["viewbox"] = viewbox
1325
1226
 
1326
1227
  if self.chart_type == "Synastry":
1327
- td["stringTitle"] = f"{self.name} {self.language_settings['and_word']} {self.t_user.name}"
1228
+ td["stringTitle"] = f"{self.user.name} {self.language_settings['and_word']} {self.t_user.name}"
1328
1229
 
1329
1230
  elif self.chart_type == "Transit":
1330
1231
  td["stringTitle"] = f"{self.language_settings['transits']} {self.t_user.day}/{self.t_user.month}/{self.t_user.year}"
1331
1232
 
1332
1233
  else:
1333
- td["stringTitle"] = self.name
1234
+ td["stringTitle"] = self.user.name
1334
1235
 
1335
1236
  # Tipo di carta
1336
- if self.chart_type == "Synastry" or self.name == "Transit":
1337
- td["stringName"] = f"{self.name}:"
1237
+ if self.chart_type == "Synastry" or self.chart_type == "Transit":
1238
+ td["stringName"] = f"{self.user.name}:"
1338
1239
  else:
1339
1240
  td["stringName"] = f'{self.language_settings["info"]}:'
1340
1241
 
@@ -1418,9 +1319,20 @@ class KerykeionChartSVG:
1418
1319
  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}"
1419
1320
 
1420
1321
  else:
1421
- td["stringLat"] = f"{self.language_settings['latitude']}: {self._lat2str(self.geolat)}"
1422
- td["stringLon"] = f"{self.language_settings['longitude']}: {self._lon2str(self.geolon)}"
1423
- td["stringPosition"] = f"{self.language_settings['type']}: {self.charttype}"
1322
+ latitude_string = convert_latitude_coordinate_to_string(
1323
+ self.geolat,
1324
+ self.language_settings['north'],
1325
+ self.language_settings['south']
1326
+ )
1327
+ longitude_string = convert_longitude_coordinate_to_string(
1328
+ self.geolon,
1329
+ self.language_settings['east'],
1330
+ self.language_settings['west']
1331
+ )
1332
+
1333
+ td["stringLat"] = f"{self.language_settings['latitude']}: {latitude_string}"
1334
+ td["stringLon"] = f"{self.language_settings['longitude']}: {longitude_string}"
1335
+ td["stringPosition"] = f"{self.language_settings['type']}: {self.chart_type}"
1424
1336
 
1425
1337
  # paper_color_X
1426
1338
  td["paper_color_0"] = self.chart_colors_settings["paper_0"]
@@ -1448,14 +1360,22 @@ class KerykeionChartSVG:
1448
1360
  # Drawing Functions
1449
1361
  #---
1450
1362
 
1451
- # Drawing the Zodiac slices
1452
1363
  td["makeZodiac"] = self._draw_zodiac_circle_slices(r)
1364
+ td["makeHousesGrid"] = self._draw_house_grid()
1453
1365
  # TODO: Add the rest of the functions
1454
1366
  td["makeHouses"] = self._makeHouses(r)
1455
1367
  td["makePlanets"] = self._make_planets(r)
1456
- td["makeElements"] = self._makeElements(r)
1368
+ td["elements_percentages"] = draw_elements_percentages(
1369
+ self.language_settings['fire'],
1370
+ self.fire,
1371
+ self.language_settings['earth'],
1372
+ self.earth,
1373
+ self.language_settings['air'],
1374
+ self.air,
1375
+ self.language_settings['water'],
1376
+ self.water,
1377
+ )
1457
1378
  td["makePlanetGrid"] = self._makePlanetGrid()
1458
- td["makeHousesGrid"] = self._makeHousesGrid()
1459
1379
 
1460
1380
  return td
1461
1381
 
@@ -1480,7 +1400,7 @@ class KerykeionChartSVG:
1480
1400
  if not (self.template):
1481
1401
  self.template = self.makeTemplate()
1482
1402
 
1483
- self.chartname = self.output_directory / f"{self.name}{self.chart_type}Chart.svg"
1403
+ self.chartname = self.output_directory / f"{self.user.name}{self.chart_type}Chart.svg"
1484
1404
 
1485
1405
  with open(self.chartname, "w", encoding="utf-8", errors="ignore") as output_file:
1486
1406
  output_file.write(self.template)