kerykeion 4.6.2__py3-none-any.whl → 4.8.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,34 @@
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
31
+ from scour.scour import scourString
19
32
  from string import Template
20
- from typing import Union
33
+ from typing import Union, List
34
+
21
35
 
22
36
 
23
37
  class KerykeionChartSVG:
@@ -34,6 +48,7 @@ class KerykeionChartSVG:
34
48
  - new_settings_file: Set the settings file (default: kr.config.json)
35
49
  """
36
50
 
51
+ # Set at init
37
52
  first_obj: AstrologicalSubject
38
53
  second_obj: Union[AstrologicalSubject, None]
39
54
  chart_type: ChartType
@@ -41,6 +56,46 @@ class KerykeionChartSVG:
41
56
  new_settings_file: Union[Path, None]
42
57
  output_directory: Path
43
58
 
59
+ # Internal properties
60
+ fire: float
61
+ earth: float
62
+ air: float
63
+ water: float
64
+ c1: float
65
+ c2: float
66
+ c3: float
67
+ homedir: Path
68
+ xml_svg: Path
69
+ natal_width: float
70
+ full_width: float
71
+ language_settings: dict
72
+ chart_colors_settings: dict
73
+ planets_settings: dict
74
+ aspects_settings: dict
75
+ planet_in_zodiac_extra_points: int
76
+ chart_settings: dict
77
+ user: AstrologicalSubject
78
+ available_planets_setting: List[KerykeionSettingsCelestialPointModel]
79
+ transit_ring_exclude_points_names: List[str]
80
+ points_deg_ut: list
81
+ points_deg: list
82
+ points_sign: list
83
+ points_retrograde: list
84
+ houses_sign_graph: list
85
+ t_points_deg_ut: list
86
+ t_points_deg: list
87
+ t_points_sign: list
88
+ t_points_retrograde: list
89
+ t_houses_sign_graph: list
90
+ screen_width: float
91
+ screen_height: float
92
+ location: str
93
+ geolat: float
94
+ geolon: float
95
+ zoom: int
96
+ zodiac: tuple
97
+ template: str
98
+
44
99
  def __init__(
45
100
  self,
46
101
  first_obj: AstrologicalSubject,
@@ -77,7 +132,23 @@ class KerykeionChartSVG:
77
132
  continue
78
133
 
79
134
  self.available_planets_setting.append(body)
80
-
135
+
136
+ # House cusp points are excluded from the transit ring.
137
+ self.transit_ring_exclude_points_names = [
138
+ "First_House",
139
+ "Second_House",
140
+ "Third_House",
141
+ "Fourth_House",
142
+ "Fifth_House",
143
+ "Sixth_House",
144
+ "Seventh_House",
145
+ "Eighth_House",
146
+ "Ninth_House",
147
+ "Tenth_House",
148
+ "Eleventh_House",
149
+ "Twelfth_House"
150
+ ]
151
+
81
152
  # Available bodies
82
153
  available_celestial_points = []
83
154
  for body in self.available_planets_setting:
@@ -152,57 +223,15 @@ class KerykeionChartSVG:
152
223
  self.screen_width = 1200
153
224
  self.screen_height = 772.2
154
225
 
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
226
  # 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
227
+ self.location = self.user.city
228
+ self.geolat = self.user.lat
229
+ self.geolon = self.user.lng
230
+
231
+ logging.info(f"{self.user.name} birth location: {self.location}, {self.geolat}, {self.geolon}")
193
232
 
194
233
  if self.chart_type == "Transit":
195
- self.t_geolon = self.geolon
196
- self.t_geolat = self.geolat
197
- self.t_altitude = self.altitude
198
234
  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
235
 
207
236
  # configuration
208
237
  # ZOOM 1 = 100%
@@ -223,8 +252,7 @@ class KerykeionChartSVG:
223
252
  {"name": "pisces", "element": "water"},
224
253
  )
225
254
 
226
- # Immediately generate template.
227
- self.template = self.makeTemplate()
255
+ self.template = None
228
256
 
229
257
  def set_output_directory(self, dir_path: Path) -> None:
230
258
  """
@@ -247,130 +275,6 @@ class KerykeionChartSVG:
247
275
  self.planet_in_zodiac_extra_points = settings["general_settings"]["planet_in_zodiac_extra_points"]
248
276
  self.chart_settings = settings["chart_settings"]
249
277
 
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
278
  def _draw_zodiac_circle_slices(self, r):
375
279
  """
376
280
  Generate the SVG string representing the zodiac circle
@@ -388,7 +292,7 @@ class KerykeionChartSVG:
388
292
  output += draw_zodiac_slice(
389
293
  c1=self.c1,
390
294
  chart_type=self.chart_type,
391
- sixth_house_degree_ut=self.user.houses_degree_ut[6],
295
+ seventh_house_degree_ut=self.user.houses_degree_ut[6],
392
296
  num=i,
393
297
  r=r,
394
298
  style=f'fill:{self.chart_colors_settings[f"zodiac_bg_{i}"]}; fill-opacity: 0.5;',
@@ -484,44 +388,50 @@ class KerykeionChartSVG:
484
388
 
485
389
  return path
486
390
 
487
- def _value_element_from_planet(self, i):
391
+ def _calculate_elements_points_from_planets(self):
488
392
  """
489
393
  Calculate chart element points from a planet.
490
394
  """
395
+
396
+ for i in range(len(self.available_planets_setting)):
397
+ # element: get extra points if planet is in own zodiac sign.
398
+ related_zodiac_signs = self.available_planets_setting[i]["related_zodiac_signs"]
399
+ cz = self.points_sign[i]
400
+ extra_points = 0
401
+ if related_zodiac_signs != []:
402
+ for e in range(len(related_zodiac_signs)):
403
+ if int(related_zodiac_signs[e]) == int(cz):
404
+ extra_points = self.planet_in_zodiac_extra_points
491
405
 
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
406
+ ele = self.zodiac[self.points_sign[i]]["element"]
407
+ if ele == "fire":
408
+ self.fire = self.fire + self.available_planets_setting[i]["element_points"] + extra_points
504
409
 
505
- elif ele == "earth":
506
- self.earth = self.earth + self.available_planets_setting[i]["element_points"] + extra_points
410
+ elif ele == "earth":
411
+ self.earth = self.earth + self.available_planets_setting[i]["element_points"] + extra_points
507
412
 
508
- elif ele == "air":
509
- self.air = self.air + self.available_planets_setting[i]["element_points"] + extra_points
413
+ elif ele == "air":
414
+ self.air = self.air + self.available_planets_setting[i]["element_points"] + extra_points
510
415
 
511
- elif ele == "water":
512
- self.water = self.water + self.available_planets_setting[i]["element_points"] + extra_points
416
+ elif ele == "water":
417
+ self.water = self.water + self.available_planets_setting[i]["element_points"] + extra_points
513
418
 
514
419
  def _make_planets(self, r):
515
420
  planets_degut = {}
516
421
  diff = range(len(self.available_planets_setting))
517
422
 
518
423
  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
424
+ # list of planets sorted by degree
425
+ logging.debug(f"planet: {i}, degree: {self.points_deg_ut[i]}")
426
+ planets_degut[self.points_deg_ut[i]] = i
523
427
 
524
- self._value_element_from_planet(i)
428
+ """
429
+ FIXME: The planets_degut is a dictionary like:
430
+ {planet_degree: planet_index}
431
+ It should be replaced bu points_deg_ut
432
+ print(self.points_deg_ut)
433
+ print(planets_degut)
434
+ """
525
435
 
526
436
  output = ""
527
437
  keys = list(planets_degut.keys())
@@ -703,14 +613,15 @@ class KerykeionChartSVG:
703
613
  if self.chart_type == "Transit" or self.chart_type == "Synastry":
704
614
  group_offset = {}
705
615
  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)
616
+ list_range = len(self.available_planets_setting)
617
+
710
618
  for i in range(list_range):
619
+ if self.chart_type == "Transit" and self.available_planets_setting[i]['name'] in self.transit_ring_exclude_points_names:
620
+ continue
621
+
711
622
  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
623
+ t_planets_degut[self.t_points_deg_ut[i]] = i
624
+
714
625
  t_keys = list(t_planets_degut.keys())
715
626
  t_keys.sort()
716
627
 
@@ -752,7 +663,12 @@ class KerykeionChartSVG:
752
663
  group_offset[groups[i][3]] = 2.0
753
664
 
754
665
  switch = 0
666
+
667
+ # Transit planets loop
755
668
  for e in range(len(t_keys)):
669
+ if self.chart_type == "Transit" and self.available_planets_setting[e]["name"] in self.transit_ring_exclude_points_names:
670
+ continue
671
+
756
672
  i = t_planets_degut[t_keys[e]]
757
673
 
758
674
  if 22 < i < 27:
@@ -764,20 +680,21 @@ class KerykeionChartSVG:
764
680
  rplanet = 26
765
681
  switch = 1
766
682
 
683
+ # Transit planet name
767
684
  zeropoint = 360 - self.user.houses_degree_ut[6]
768
685
  t_offset = zeropoint + self.t_points_deg_ut[i]
769
686
  if t_offset > 360:
770
687
  t_offset = t_offset - 360
771
688
  planet_x = sliceToX(0, (r - rplanet), t_offset) + rplanet
772
689
  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>'
690
+ 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
691
 
775
- # transit planet line
692
+ # Transit planet line
776
693
  x1 = sliceToX(0, r + 3, t_offset) - 3
777
694
  y1 = sliceToY(0, r + 3, t_offset) - 3
778
695
  x2 = sliceToX(0, r - 3, t_offset) + 3
779
696
  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;"/>'
697
+ 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
698
 
782
699
  # transit planet degree text
783
700
  rotate = self.user.houses_degree_ut[0] - self.t_points_deg_ut[i]
@@ -801,7 +718,7 @@ class KerykeionChartSVG:
801
718
  degree = int(t_offset)
802
719
  output += f'<g transform="translate({deg_x},{deg_y})">'
803
720
  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")}'
721
+ 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
722
  output += "</text></g>"
806
723
 
807
724
  # check transit
@@ -983,12 +900,13 @@ class KerykeionChartSVG:
983
900
  def _makeAspects(self, r, ar):
984
901
  out = ""
985
902
  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"],
903
+ out += draw_aspect_line(
904
+ r=r,
905
+ ar=ar,
906
+ degA=element["p1_abs_pos"],
907
+ degB=element["p2_abs_pos"],
908
+ color=self.aspects_settings[element["aid"]]["color"],
909
+ seventh_house_degree_ut=self.user.seventh_house.abs_pos
992
910
  )
993
911
 
994
912
  return out
@@ -1004,24 +922,22 @@ class KerykeionChartSVG:
1004
922
  counter = 0
1005
923
  for a in revr:
1006
924
  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"]}" />'
925
+ out += f'<rect x="{xindent}" y="{yindent}" width="{box}" height="{box}" style="{style}"/>'
926
+ 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"]}" />'
927
+
928
+ xindent = xindent + box
929
+ yindent = yindent - box
930
+ revr2 = list(range(a))
931
+ revr2.reverse()
932
+ xorb = xindent
933
+ yorb = yindent + box
934
+ for b in revr2:
935
+ out += f'<rect x="{xorb}" y="{yorb}" width="{box}" height="{box}" style="{style}"/>'
936
+
937
+ xorb = xorb + box
938
+ for element in self.aspects_list:
939
+ if (element["p1"] == a and element["p2"] == b) or (element["p1"] == b and element["p2"] == a):
940
+ out += f'<use x="{xorb-box+1}" y="{yorb+1}" xlink:href="#orb{element["aspect_degrees"]}" />'
1025
941
 
1026
942
  return out
1027
943
 
@@ -1032,12 +948,13 @@ class KerykeionChartSVG:
1032
948
  self.aspects_list = SynastryAspects(self.user, self.t_user, new_settings_file=self.new_settings_file).relevant_aspects
1033
949
 
1034
950
  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"],
951
+ out += draw_aspect_line(
952
+ r=r,
953
+ ar=ar,
954
+ degA=element["p1_abs_pos"],
955
+ degB=element["p2_abs_pos"],
956
+ color=self.aspects_settings[element["aid"]]["color"],
957
+ seventh_house_degree_ut=self.user.seventh_house.abs_pos
1041
958
  )
1042
959
 
1043
960
  return out
@@ -1088,36 +1005,20 @@ class KerykeionChartSVG:
1088
1005
 
1089
1006
  out += "</g>"
1090
1007
  # 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>'
1008
+ 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
1009
  # line
1093
1010
  out += "</g>"
1094
1011
  line = line + 14
1095
1012
  out += "</g>"
1096
1013
  return out
1097
1014
 
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
1015
  def _makePlanetGrid(self):
1115
1016
  li = 10
1116
1017
  offset = 0
1117
1018
 
1118
1019
  out = '<g transform="translate(500,-20)">'
1119
1020
  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>'
1021
+ 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
1022
  out += "</g>"
1122
1023
 
1123
1024
  end_of_line = None
@@ -1140,7 +1041,7 @@ class KerykeionChartSVG:
1140
1041
  out += f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g>'
1141
1042
 
1142
1043
  # 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>'
1044
+ 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
1045
 
1145
1046
  # zodiac
1146
1047
  out += f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.points_sign[i]]["name"]}" /></g>'
@@ -1172,27 +1073,26 @@ class KerykeionChartSVG:
1172
1073
  t_li = 10
1173
1074
  t_offset = -120
1174
1075
 
1175
- if self.available_planets_setting[i]["is_active"] == 1:
1176
- # start of line
1177
- out += f'<g transform="translate({t_offset},{t_li})">'
1076
+ # start of line
1077
+ out += f'<g transform="translate({t_offset},{t_li})">'
1178
1078
 
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>'
1079
+ # planet text
1080
+ 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>'
1081
+ # planet symbol
1082
+ out += f'<g transform="translate(5,-8)"><use transform="scale(0.4)" xlink:href="#{self.available_planets_setting[i]["name"]}" /></g>'
1083
+ # planet degree
1084
+ 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>'
1085
+ # zodiac
1086
+ out += f'<g transform="translate(60,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.t_points_sign[i]]["name"]}" /></g>'
1187
1087
 
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>'
1088
+ # planet retrograde
1089
+ if self.t_points_retrograde[i]:
1090
+ out += '<g transform="translate(74,-6)"><use transform="scale(.5)" xlink:href="#retrograde" /></g>'
1191
1091
 
1192
- # end of line
1193
- out += end_of_line
1092
+ # end of line
1093
+ out += end_of_line
1194
1094
 
1195
- t_li = t_li + offset_between_lines
1095
+ t_li = t_li + offset_between_lines
1196
1096
 
1197
1097
  if end_of_line is None:
1198
1098
  raise KerykeionException("End of line not found")
@@ -1201,6 +1101,12 @@ class KerykeionChartSVG:
1201
1101
  return out
1202
1102
 
1203
1103
  def _draw_house_grid(self):
1104
+ """
1105
+ Generate SVG code for a grid of astrological houses.
1106
+
1107
+ Returns:
1108
+ str: The SVG code for the grid of houses.
1109
+ """
1204
1110
  out = '<g transform="translate(600,-20)">'
1205
1111
 
1206
1112
  li = 10
@@ -1212,7 +1118,7 @@ class KerykeionChartSVG:
1212
1118
  out += f'<g transform="translate(0,{li})">'
1213
1119
  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>'
1214
1120
  out += f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.houses_sign_graph[i]]["name"]}" /></g>'
1215
- out += f'<text x="53" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;"> {self._dec2deg(self.user.houses_list[i]["position"])}</text>'
1121
+ 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>'
1216
1122
  out += "</g>"
1217
1123
  li = li + 14
1218
1124
 
@@ -1229,7 +1135,7 @@ class KerykeionChartSVG:
1229
1135
  out += '<g transform="translate(0,' + str(li) + ')">'
1230
1136
  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>'
1231
1137
  out += f'<g transform="translate(40,-8)"><use transform="scale(0.3)" xlink:href="#{self.zodiac[self.t_houses_sign_graph[i]]["name"]}" /></g>'
1232
- out += f'<text x="53" style="fill:{self.chart_colors_settings["paper_0"]}; font-size: 10px;"> {self._dec2deg(self.t_user.houses_list[i]["position"])}</text>'
1138
+ 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>'
1233
1139
  out += "</g>"
1234
1140
  li = li + 14
1235
1141
  out += "</g>"
@@ -1244,6 +1150,9 @@ class KerykeionChartSVG:
1244
1150
  self.air = 0.0
1245
1151
  self.water = 0.0
1246
1152
 
1153
+ # Calculate the elements points
1154
+ self._calculate_elements_points_from_planets()
1155
+
1247
1156
  # width and height from screen
1248
1157
  ratio = float(self.screen_width) / float(self.screen_height)
1249
1158
  if ratio < 1.3: # 1280x1024
@@ -1278,8 +1187,8 @@ class KerykeionChartSVG:
1278
1187
 
1279
1188
  # transit
1280
1189
  if self.chart_type == "Transit" or self.chart_type == "Synastry":
1281
- td["transitRing"] = self._transitRing(r)
1282
- td["degreeRing"] = self._degreeTransitRing(r)
1190
+ td["transitRing"] = draw_transit_ring(r, self.chart_colors_settings["paper_1"], self.chart_colors_settings["zodiac_transit_ring_3"])
1191
+ td["degreeRing"] = draw_transit_ring_degree_steps(r, self.user.seventh_house.abs_pos)
1283
1192
 
1284
1193
  # circles
1285
1194
  td["c1"] = f'cx="{r}" cy="{r}" r="{r - 36}"'
@@ -1296,7 +1205,7 @@ class KerykeionChartSVG:
1296
1205
  td["chart_width"] = self.full_width
1297
1206
  else:
1298
1207
  td["transitRing"] = ""
1299
- td["degreeRing"] = self._degreeRing(r)
1208
+ td["degreeRing"] = draw_degree_ring(r, self.c1, self.user.seventh_house.abs_pos, self.chart_colors_settings["paper_0"])
1300
1209
 
1301
1210
  # circles
1302
1211
  td["c1"] = f'cx="{r}" cy="{r}" r="{r - self.c1}"'
@@ -1317,17 +1226,17 @@ class KerykeionChartSVG:
1317
1226
  td["viewbox"] = viewbox
1318
1227
 
1319
1228
  if self.chart_type == "Synastry":
1320
- td["stringTitle"] = f"{self.name} {self.language_settings['and_word']} {self.t_user.name}"
1229
+ td["stringTitle"] = f"{self.user.name} {self.language_settings['and_word']} {self.t_user.name}"
1321
1230
 
1322
1231
  elif self.chart_type == "Transit":
1323
1232
  td["stringTitle"] = f"{self.language_settings['transits']} {self.t_user.day}/{self.t_user.month}/{self.t_user.year}"
1324
1233
 
1325
1234
  else:
1326
- td["stringTitle"] = self.name
1235
+ td["stringTitle"] = self.user.name
1327
1236
 
1328
1237
  # Tipo di carta
1329
- if self.chart_type == "Synastry" or self.name == "Transit":
1330
- td["stringName"] = f"{self.name}:"
1238
+ if self.chart_type == "Synastry" or self.chart_type == "Transit":
1239
+ td["stringName"] = f"{self.user.name}:"
1331
1240
  else:
1332
1241
  td["stringName"] = f'{self.language_settings["info"]}:'
1333
1242
 
@@ -1411,9 +1320,20 @@ class KerykeionChartSVG:
1411
1320
  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}"
1412
1321
 
1413
1322
  else:
1414
- td["stringLat"] = f"{self.language_settings['latitude']}: {self._lat2str(self.geolat)}"
1415
- td["stringLon"] = f"{self.language_settings['longitude']}: {self._lon2str(self.geolon)}"
1416
- td["stringPosition"] = f"{self.language_settings['type']}: {self.charttype}"
1323
+ latitude_string = convert_latitude_coordinate_to_string(
1324
+ self.geolat,
1325
+ self.language_settings['north'],
1326
+ self.language_settings['south']
1327
+ )
1328
+ longitude_string = convert_longitude_coordinate_to_string(
1329
+ self.geolon,
1330
+ self.language_settings['east'],
1331
+ self.language_settings['west']
1332
+ )
1333
+
1334
+ td["stringLat"] = f"{self.language_settings['latitude']}: {latitude_string}"
1335
+ td["stringLon"] = f"{self.language_settings['longitude']}: {longitude_string}"
1336
+ td["stringPosition"] = f"{self.language_settings['type']}: {self.chart_type}"
1417
1337
 
1418
1338
  # paper_color_X
1419
1339
  td["paper_color_0"] = self.chart_colors_settings["paper_0"]
@@ -1446,12 +1366,21 @@ class KerykeionChartSVG:
1446
1366
  # TODO: Add the rest of the functions
1447
1367
  td["makeHouses"] = self._makeHouses(r)
1448
1368
  td["makePlanets"] = self._make_planets(r)
1449
- td["makeElements"] = self._makeElements(r)
1369
+ td["elements_percentages"] = draw_elements_percentages(
1370
+ self.language_settings['fire'],
1371
+ self.fire,
1372
+ self.language_settings['earth'],
1373
+ self.earth,
1374
+ self.language_settings['air'],
1375
+ self.air,
1376
+ self.language_settings['water'],
1377
+ self.water,
1378
+ )
1450
1379
  td["makePlanetGrid"] = self._makePlanetGrid()
1451
1380
 
1452
1381
  return td
1453
1382
 
1454
- def makeTemplate(self):
1383
+ def makeTemplate(self, minify: bool = False) -> str:
1455
1384
  """Creates the template for the SVG file"""
1456
1385
  td = self._createTemplateDictionary()
1457
1386
 
@@ -1464,15 +1393,22 @@ class KerykeionChartSVG:
1464
1393
  logging.debug(f"Template dictionary keys: {td.keys()}")
1465
1394
 
1466
1395
  self._createTemplateDictionary()
1467
- return template.replace('"', "'")
1468
1396
 
1469
- def makeSVG(self) -> None:
1397
+ if minify:
1398
+ template = scourString(template).replace('"', "'").replace("\n", "").replace("\t","").replace(" ", "").replace(" ", "")
1399
+
1400
+ else:
1401
+ template = template.replace('"', "'")
1402
+
1403
+ return template
1404
+
1405
+ def makeSVG(self, minify: bool = False):
1470
1406
  """Prints out the SVG file in the specifide folder"""
1471
1407
 
1472
1408
  if not (self.template):
1473
- self.template = self.makeTemplate()
1409
+ self.template = self.makeTemplate(minify)
1474
1410
 
1475
- self.chartname = self.output_directory / f"{self.name}{self.chart_type}Chart.svg"
1411
+ self.chartname = self.output_directory / f"{self.user.name}{self.chart_type}Chart.svg"
1476
1412
 
1477
1413
  with open(self.chartname, "w", encoding="utf-8", errors="ignore") as output_file:
1478
1414
  output_file.write(self.template)
@@ -1497,8 +1433,9 @@ if __name__ == "__main__":
1497
1433
 
1498
1434
  # Synastry Chart
1499
1435
  synastry_chart = KerykeionChartSVG(first, "Synastry", second)
1500
- synastry_chart.makeSVG()
1436
+ synastry_chart.makeSVG(minify=True)
1501
1437
 
1502
1438
  # Transits Chart
1503
1439
  transits_chart = KerykeionChartSVG(first, "Transit", second)
1504
- transits_chart.makeSVG()
1440
+ transits_chart.makeSVG(minify=True)
1441
+