kerykeion 4.8.1__py3-none-any.whl → 4.14.2__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.

@@ -27,12 +27,18 @@ from kerykeion.charts.charts_utils import (
27
27
  draw_degree_ring,
28
28
  draw_transit_ring,
29
29
  draw_first_circle,
30
- draw_second_circle
30
+ draw_second_circle,
31
+ draw_third_circle,
32
+ draw_aspect_grid,
33
+ draw_houses_cusps_and_text_number,
34
+ draw_aspect_transit_grid,
35
+ draw_moon_phase
31
36
  )
32
37
  from pathlib import Path
33
38
  from scour.scour import scourString
34
39
  from string import Template
35
40
  from typing import Union, List
41
+ from datetime import datetime
36
42
 
37
43
 
38
44
 
@@ -45,9 +51,9 @@ class KerykeionChartSVG:
45
51
  - first_obj: First kerykeion object
46
52
  - chart_type: Natal, ExternalNatal, Transit, Synastry (Default: Type="Natal").
47
53
  - second_obj: Second kerykeion object (Not required if type is Natal)
48
- - new_output_directory: Set the output directory (default: output_directory)
49
- - lang: language settings (default: "EN")
50
- - new_settings_file: Set the settings file (default: kr.config.json)
54
+ - new_output_directory: Set the output directory (default: home directory).
55
+ - new_settings_file: Set the settings file (default: kr.config.json).
56
+ In the settings file you can set the language, colors, planets, aspects, etc.
51
57
  """
52
58
 
53
59
  # Constants
@@ -97,7 +103,6 @@ class KerykeionChartSVG:
97
103
  location: str
98
104
  geolat: float
99
105
  geolon: float
100
- zoom: int
101
106
  zodiac: tuple
102
107
  template: str
103
108
 
@@ -218,7 +223,6 @@ class KerykeionChartSVG:
218
223
  self.t_houses_sign_graph.append(h["sign_num"])
219
224
 
220
225
  # screen size
221
-
222
226
  self.height = self._DEFAULT_HEIGHT
223
227
  if self.chart_type == "Synastry" or self.chart_type == "Transit":
224
228
  self.width = self._DEFAULT_FULL_WIDTH
@@ -303,93 +307,6 @@ class KerykeionChartSVG:
303
307
 
304
308
  return output
305
309
 
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
310
  def _calculate_elements_points_from_planets(self):
394
311
  """
395
312
  Calculate chart element points from a planet.
@@ -608,8 +525,12 @@ class KerykeionChartSVG:
608
525
 
609
526
  else:
610
527
  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>'
528
+
529
+ planet_details = self.user[self.available_planets_setting[i]["name"].lower()]
530
+
531
+ output += f'<g kr:node="ChartPoint" kr:house="{planet_details['house']}" kr:sign="{planet_details['sign']}" kr:slug="{planet_details['name']}" transform="translate(-{12 * scale},-{12 * scale}) scale({scale})">'
532
+ output += f'<use x="{planet_x * (1/scale)}" y="{planet_y * (1/scale)}" xlink:href="#{self.available_planets_setting[i]["name"]}" />'
533
+ output += f'</g>'
613
534
 
614
535
  # make transit degut and display planets
615
536
  if self.chart_type == "Transit" or self.chart_type == "Synastry":
@@ -751,274 +672,42 @@ class KerykeionChartSVG:
751
672
 
752
673
  return output
753
674
 
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
675
  # Aspect and aspect grid functions for natal type charts.
902
- def _makeAspects(self, r, ar):
676
+ def _draw_all_aspects_lines(self, r, ar):
903
677
  out = ""
904
- for element in self.aspects_list:
678
+ for aspect in self.aspects_list:
905
679
  out += draw_aspect_line(
906
680
  r=r,
907
681
  ar=ar,
908
- degA=element["p1_abs_pos"],
909
- degB=element["p2_abs_pos"],
910
- color=self.aspects_settings[element["aid"]]["color"],
682
+ aspect=aspect,
683
+ color=self.aspects_settings[aspect["aid"]]["color"],
911
684
  seventh_house_degree_ut=self.user.seventh_house.abs_pos
912
685
  )
913
686
 
914
687
  return out
915
688
 
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
689
  # Aspect and aspect grid functions for transit type charts
947
- def _makeAspectsTransit(self, r, ar):
690
+ def _draw_all_transit_aspects_lines(self, r, ar):
948
691
  out = ""
949
692
 
950
693
  self.aspects_list = SynastryAspects(self.user, self.t_user, new_settings_file=self.new_settings_file).relevant_aspects
951
694
 
952
- for element in self.aspects_list:
695
+ for aspect in self.aspects_list:
953
696
  out += draw_aspect_line(
954
697
  r=r,
955
698
  ar=ar,
956
- degA=element["p1_abs_pos"],
957
- degB=element["p2_abs_pos"],
958
- color=self.aspects_settings[element["aid"]]["color"],
699
+ aspect=aspect,
700
+ color=self.aspects_settings[aspect["aid"]]["color"],
959
701
  seventh_house_degree_ut=self.user.seventh_house.abs_pos
960
702
  )
961
703
 
962
704
  return out
963
705
 
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
706
  def _makePlanetGrid(self):
1018
707
  li = 10
1019
708
  offset = 0
1020
709
 
1021
- out = '<g transform="translate(500,-20)">'
710
+ out = '<g transform="translate(510,-20)">'
1022
711
  out += '<g transform="translate(140, -15)">'
1023
712
  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
713
  out += "</g>"
@@ -1102,14 +791,14 @@ class KerykeionChartSVG:
1102
791
  out += end_of_line
1103
792
  return out
1104
793
 
1105
- def _draw_house_grid(self):
794
+ def _makeHouseGrid(self):
1106
795
  """
1107
796
  Generate SVG code for a grid of astrological houses.
1108
797
 
1109
798
  Returns:
1110
799
  str: The SVG code for the grid of houses.
1111
800
  """
1112
- out = '<g transform="translate(600,-20)">'
801
+ out = '<g transform="translate(610,-20)">'
1113
802
 
1114
803
  li = 10
1115
804
  for i in range(12):
@@ -1127,8 +816,10 @@ class KerykeionChartSVG:
1127
816
  out += "</g>"
1128
817
 
1129
818
  if self.chart_type == "Synastry":
1130
- out += '<g transform="translate(840, -20)">'
819
+ out += '<!-- Synastry Houses -->'
820
+ out += '<g transform="translate(850, -20)">'
1131
821
  li = 10
822
+
1132
823
  for i in range(12):
1133
824
  if i < 9:
1134
825
  cusp = "&#160;&#160;" + str(i + 1)
@@ -1155,22 +846,18 @@ class KerykeionChartSVG:
1155
846
  # Calculate the elements points
1156
847
  self._calculate_elements_points_from_planets()
1157
848
 
1158
- # Viewbox and sizing
1159
- svgHeight = "100%"
1160
- svgWidth = "100%"
1161
849
  rotate = "0"
1162
- translate = "0"
1163
-
1164
- # To increase the size of the chart, change the viewbox
1165
- if self.chart_type == "Natal" or self.chart_type == "ExternalNatal":
1166
- viewbox = self.chart_settings["basic_chart_viewBox"]
1167
- else:
1168
- viewbox = self.chart_settings["wide_chart_viewBox"]
1169
850
 
1170
851
  # template dictionary
1171
852
  td: ChartTemplateDictionary = dict() # type: ignore
1172
853
  r = 240
1173
854
 
855
+ # To increase the size of the chart, change the viewbox
856
+ if self.chart_type == "Natal" or self.chart_type == "ExternalNatal":
857
+ td['viewbox'] = self.chart_settings["basic_chart_viewBox"]
858
+ else:
859
+ td['viewbox'] = self.chart_settings["wide_chart_viewBox"]
860
+
1174
861
  if self.chart_type == "ExternalNatal":
1175
862
  self.c1 = 56
1176
863
  self.c2 = 92
@@ -1188,36 +875,33 @@ class KerykeionChartSVG:
1188
875
  # circles
1189
876
  td["first_circle"] = draw_first_circle(r, self.chart_colors_settings["zodiac_transit_ring_2"], self.chart_type)
1190
877
  td["second_circle"] = draw_second_circle(r, self.chart_colors_settings['zodiac_transit_ring_1'], self.chart_colors_settings['paper_1'], self.chart_type)
878
+ td['third_circle'] = draw_third_circle(r, self.chart_colors_settings['zodiac_transit_ring_0'], self.chart_colors_settings['paper_1'], self.chart_type)
1191
879
 
1192
- td["c3"] = 'cx="' + str(r) + '" cy="' + str(r) + '" r="' + str(r - 160) + '"'
1193
- 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"
880
+ td["makeAspects"] = self._draw_all_transit_aspects_lines(r, (r - 160))
1194
881
 
1195
- td["makeAspects"] = self._makeAspectsTransit(r, (r - 160))
1196
- td["makeAspectGrid"] = self._makeAspectTransitGrid(r)
1197
- td["makePatterns"] = ""
882
+ td["makeAspectGrid"] = draw_aspect_transit_grid(
883
+ self.language_settings["aspects"],
884
+ self.aspects_list,
885
+ self.planets_settings,
886
+ self.aspects_settings
887
+ )
888
+
889
+ # Natal, External Natal
1198
890
  else:
1199
891
  td["transitRing"] = ""
1200
892
  td["degreeRing"] = draw_degree_ring(r, self.c1, self.user.seventh_house.abs_pos, self.chart_colors_settings["paper_0"])
1201
893
 
1202
894
  td['first_circle'] = draw_first_circle(r, self.chart_colors_settings["zodiac_radix_ring_2"], self.chart_type, self.c1)
1203
-
1204
895
  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)
896
+ td['third_circle'] = draw_third_circle(r, self.chart_colors_settings["zodiac_radix_ring_0"], self.chart_colors_settings["paper_1"], self.chart_type, self.c3)
1205
897
 
1206
- td["c3"] = f'cx="{r}" cy="{r}" r="{r - self.c3}"'
1207
- 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'
1208
-
1209
- td["makeAspects"] = self._makeAspects(r, (r - self.c3))
1210
- td["makeAspectGrid"] = self._makeAspectGrid(r)
1211
- td["makePatterns"] = self._makePatterns()
1212
-
898
+ td["makeAspects"] = self._draw_all_aspects_lines(r, (r - self.c3))
899
+ td["makeAspectGrid"] = draw_aspect_grid(self.chart_colors_settings['paper_0'], self.available_planets_setting, self.aspects_list)
900
+
1213
901
  td["chart_height"] = self.height
1214
902
  td["chart_width"] = self.width
1215
- td["circleX"] = str(0)
1216
- td["circleY"] = str(0)
1217
- td["svgWidth"] = str(svgWidth)
1218
- td["svgHeight"] = str(svgHeight)
1219
- td["viewbox"] = viewbox
1220
903
 
904
+ # Chart Title
1221
905
  if self.chart_type == "Synastry":
1222
906
  td["stringTitle"] = f"{self.user.name} {self.language_settings['and_word']} {self.t_user.name}"
1223
907
 
@@ -1227,72 +911,32 @@ class KerykeionChartSVG:
1227
911
  else:
1228
912
  td["stringTitle"] = self.user.name
1229
913
 
1230
- # Tipo di carta
914
+ # Chart Name
1231
915
  if self.chart_type == "Synastry" or self.chart_type == "Transit":
1232
916
  td["stringName"] = f"{self.user.name}:"
1233
917
  else:
1234
918
  td["stringName"] = f'{self.language_settings["info"]}:'
1235
919
 
1236
- # bottom left
1237
- td["bottomLeft1"] = ""
1238
- td["bottomLeft2"] = ""
1239
- td["bottomLeft3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get("day", "Day")} {self.user.lunar_phase.get("moon_phase", "")}'
1240
- td["bottomLeft4"] = ""
1241
-
1242
- # lunar phase
1243
- deg = self.user.lunar_phase["degrees_between_s_m"]
1244
-
1245
- lffg = None
1246
- lfbg = None
1247
- lfcx = None
1248
- lfr = None
1249
-
1250
- if deg < 90.0:
1251
- maxr = deg
1252
- if deg > 80.0:
1253
- maxr = maxr * maxr
1254
- lfcx = 20.0 + (deg / 90.0) * (maxr + 10.0)
1255
- lfr = 10.0 + (deg / 90.0) * maxr
1256
- lffg = self.chart_colors_settings["lunar_phase_0"]
1257
- lfbg = self.chart_colors_settings["lunar_phase_1"]
1258
-
1259
- elif deg < 180.0:
1260
- maxr = 180.0 - deg
1261
- if deg < 100.0:
1262
- maxr = maxr * maxr
1263
- lfcx = 20.0 + ((deg - 90.0) / 90.0 * (maxr + 10.0)) - (maxr + 10.0)
1264
- lfr = 10.0 + maxr - ((deg - 90.0) / 90.0 * maxr)
1265
- lffg = self.chart_colors_settings["lunar_phase_1"]
1266
- lfbg = self.chart_colors_settings["lunar_phase_0"]
1267
-
1268
- elif deg < 270.0:
1269
- maxr = deg - 180.0
1270
- if deg > 260.0:
1271
- maxr = maxr * maxr
1272
- lfcx = 20.0 + ((deg - 180.0) / 90.0 * (maxr + 10.0))
1273
- lfr = 10.0 + ((deg - 180.0) / 90.0 * maxr)
1274
- lffg, lfbg = self.chart_colors_settings["lunar_phase_1"], self.chart_colors_settings["lunar_phase_0"]
1275
-
1276
- elif deg < 361:
1277
- maxr = 360.0 - deg
1278
- if deg < 280.0:
1279
- maxr = maxr * maxr
1280
- lfcx = 20.0 + ((deg - 270.0) / 90.0 * (maxr + 10.0)) - (maxr + 10.0)
1281
- lfr = 10.0 + maxr - ((deg - 270.0) / 90.0 * maxr)
1282
- lffg, lfbg = self.chart_colors_settings["lunar_phase_0"], self.chart_colors_settings["lunar_phase_1"]
1283
-
1284
- if lffg is None or lfbg is None or lfcx is None or lfr is None:
1285
- raise KerykeionException("Lunar phase error")
1286
-
1287
- td["lunar_phase_fg"] = lffg
1288
- td["lunar_phase_bg"] = lfbg
1289
- td["lunar_phase_cx"] = lfcx
1290
- td["lunar_phase_r"] = lfr
1291
- td["lunar_phase_outline"] = self.chart_colors_settings["lunar_phase_2"]
1292
-
1293
- # rotation based on latitude
1294
- td["lunar_phase_rotate"] = -90.0 - self.geolat
920
+ # Bottom Left Corner
921
+ if self.chart_type == "Natal" or self.chart_type == "ExternalNatal" or self.chart_type == "Synastry":
922
+ td["bottomLeft0"] = f"{self.user.zodiac_type if self.user.zodiac_type == 'Tropic' else self.user.zodiac_type + ' ' + self.user.sidereal_mode}"
923
+ td["bottomLeft1"] = f"{self.user.houses_system_name}"
924
+ td["bottomLeft2"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.language_settings.get("day", "Day")} {self.user.lunar_phase.get("moon_phase", "")}'
925
+ td["bottomLeft3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.user.lunar_phase.moon_phase_name}'
926
+ td["bottomLeft4"] = f'{self.user.perspective_type}'
1295
927
 
928
+ else:
929
+ td["bottomLeft0"] = f"{self.user.zodiac_type if self.user.zodiac_type == 'Tropic' else self.user.zodiac_type + ' ' + self.user.sidereal_mode}"
930
+ td["bottomLeft1"] = f"{self.user.houses_system_name}"
931
+ 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", "")}'
932
+ td["bottomLeft3"] = f'{self.language_settings.get("lunar_phase", "Lunar Phase")}: {self.t_user.lunar_phase.moon_phase_name}'
933
+ td["bottomLeft4"] = f'{self.t_user.perspective_type}'
934
+
935
+ td['moon_phase'] = draw_moon_phase(
936
+ self.user.lunar_phase["degrees_between_s_m"],
937
+ self.geolat
938
+ )
939
+
1296
940
  # stringlocation
1297
941
  if len(self.location) > 35:
1298
942
  split = self.location.split(",")
@@ -1305,8 +949,6 @@ class KerykeionChartSVG:
1305
949
  else:
1306
950
  td["stringLocation"] = self.location
1307
951
 
1308
- td["stringDateTime"] = f"{self.user.year}-{self.user.month}-{self.user.day} {self.user.hour:02d}:{self.user.minute:02d}"
1309
-
1310
952
  if self.chart_type == "Synastry":
1311
953
  td["stringLat"] = f"{self.t_user.name}: "
1312
954
  td["stringLon"] = self.t_user.city
@@ -1346,18 +988,45 @@ class KerykeionChartSVG:
1346
988
  td[f"orb_color_{self.aspects_settings[i]['degree']}"] = self.aspects_settings[i]['color']
1347
989
 
1348
990
  # config
1349
- td["cfgZoom"] = str(self.zoom)
1350
991
  td["cfgRotate"] = rotate
1351
- td["cfgTranslate"] = translate
1352
992
 
1353
993
  # ---
1354
994
  # Drawing Functions
1355
995
  #---
1356
996
 
1357
997
  td["makeZodiac"] = self._draw_zodiac_circle_slices(r)
1358
- td["makeHousesGrid"] = self._draw_house_grid()
1359
- # TODO: Add the rest of the functions
1360
- td["makeHouses"] = self._makeHouses(r)
998
+ td["makeHousesGrid"] = self._makeHouseGrid()
999
+
1000
+ # Houses Cusps and Number
1001
+ if self.chart_type == "Transit" or self.chart_type == "Synastry":
1002
+ td["makeHouses"] = draw_houses_cusps_and_text_number(
1003
+ r=r,
1004
+ first_subject_houses_list_ut=self.user.houses_degree_ut,
1005
+ standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
1006
+ first_house_color=self.planets_settings[12]["color"],
1007
+ tenth_house_color=self.planets_settings[13]["color"],
1008
+ seventh_house_color=self.planets_settings[14]["color"],
1009
+ fourth_house_color=self.planets_settings[15]["color"],
1010
+ c1=self.c1,
1011
+ c3=self.c3,
1012
+ chart_type=self.chart_type,
1013
+ second_subject_houses_list_ut=self.t_user.houses_degree_ut,
1014
+ transit_house_cusp_color=self.chart_colors_settings["houses_transit_line"],
1015
+ )
1016
+ else:
1017
+ td["makeHouses"] = draw_houses_cusps_and_text_number(
1018
+ r=r,
1019
+ first_subject_houses_list_ut=self.user.houses_degree_ut,
1020
+ standard_house_cusp_color=self.chart_colors_settings["houses_radix_line"],
1021
+ first_house_color=self.planets_settings[12]["color"],
1022
+ tenth_house_color=self.planets_settings[13]["color"],
1023
+ seventh_house_color=self.planets_settings[14]["color"],
1024
+ fourth_house_color=self.planets_settings[15]["color"],
1025
+ c1=self.c1,
1026
+ c3=self.c3,
1027
+ chart_type=self.chart_type,
1028
+ )
1029
+
1361
1030
  td["makePlanets"] = self._make_planets(r)
1362
1031
  td["elements_percentages"] = draw_elements_percentages(
1363
1032
  self.language_settings['fire'],
@@ -1369,8 +1038,15 @@ class KerykeionChartSVG:
1369
1038
  self.language_settings['water'],
1370
1039
  self.water,
1371
1040
  )
1041
+
1372
1042
  td["makePlanetGrid"] = self._makePlanetGrid()
1373
1043
 
1044
+ # Date time String
1045
+ dt = datetime.fromisoformat(self.user.iso_formatted_local_datetime)
1046
+ custom_format = dt.strftime('%Y-%-m-%-d %H:%M [%z]') # Note the use of '-' to remove leading zeros
1047
+ custom_format = custom_format[:-3] + ':' + custom_format[-3:]
1048
+ td["stringDateTime"] = f"{custom_format}"
1049
+
1374
1050
  return td
1375
1051
 
1376
1052
  def makeTemplate(self, minify: bool = False) -> str:
@@ -1401,7 +1077,7 @@ class KerykeionChartSVG:
1401
1077
  if not (self.template):
1402
1078
  self.template = self.makeTemplate(minify)
1403
1079
 
1404
- self.chartname = self.output_directory / f"{self.user.name}{self.chart_type}Chart.svg"
1080
+ self.chartname = self.output_directory / f"{self.user.name} - {self.chart_type} Chart.svg"
1405
1081
 
1406
1082
  with open(self.chartname, "w", encoding="utf-8", errors="ignore") as output_file:
1407
1083
  output_file.write(self.template)
@@ -1413,7 +1089,7 @@ if __name__ == "__main__":
1413
1089
  from kerykeion.utilities import setup_logging
1414
1090
  setup_logging(level="debug")
1415
1091
 
1416
- first = AstrologicalSubject("John Lennon", 1940, 10, 9, 10, 30, "Liverpool", "GB")
1092
+ first = AstrologicalSubject("John Lennon", 1940, 10, 9, 18, 30, "Liverpool", "GB")
1417
1093
  second = AstrologicalSubject("Paul McCartney", 1942, 6, 18, 15, 30, "Liverpool", "GB")
1418
1094
 
1419
1095
  # Internal Natal Chart
@@ -1426,9 +1102,56 @@ if __name__ == "__main__":
1426
1102
 
1427
1103
  # Synastry Chart
1428
1104
  synastry_chart = KerykeionChartSVG(first, "Synastry", second)
1429
- synastry_chart.makeSVG(minify=True)
1105
+ synastry_chart.makeSVG()
1430
1106
 
1431
1107
  # Transits Chart
1432
1108
  transits_chart = KerykeionChartSVG(first, "Transit", second)
1433
- transits_chart.makeSVG(minify=True)
1434
-
1109
+ transits_chart.makeSVG()
1110
+
1111
+ # Sidereal Birth Chart (Lahiri)
1112
+ sidereal_subject = AstrologicalSubject("John Lennon Lahiri", 1940, 10, 9, 18, 30, "Liverpool", "GB", zodiac_type="Sidereal", sidereal_mode="LAHIRI")
1113
+ sidereal_chart = KerykeionChartSVG(sidereal_subject)
1114
+ sidereal_chart.makeSVG()
1115
+
1116
+ # Sidereal Birth Chart (Fagan-Bradley)
1117
+ sidereal_subject = AstrologicalSubject("John Lennon Fagan-Bradley", 1940, 10, 9, 18, 30, "Liverpool", "GB", zodiac_type="Sidereal", sidereal_mode="FAGAN_BRADLEY")
1118
+ sidereal_chart = KerykeionChartSVG(sidereal_subject)
1119
+ sidereal_chart.makeSVG()
1120
+
1121
+ # Sidereal Birth Chart (DeLuce)
1122
+ sidereal_subject = AstrologicalSubject("John Lennon DeLuce", 1940, 10, 9, 18, 30, "Liverpool", "GB", zodiac_type="Sidereal", sidereal_mode="DELUCE")
1123
+ sidereal_chart = KerykeionChartSVG(sidereal_subject)
1124
+ sidereal_chart.makeSVG()
1125
+
1126
+ # Sidereal Birth Chart (J2000)
1127
+ sidereal_subject = AstrologicalSubject("John Lennon J2000", 1940, 10, 9, 18, 30, "Liverpool", "GB", zodiac_type="Sidereal", sidereal_mode="J2000")
1128
+ sidereal_chart = KerykeionChartSVG(sidereal_subject)
1129
+ sidereal_chart.makeSVG()
1130
+
1131
+ # House System Morinus
1132
+ morinus_house_subject = AstrologicalSubject("John Lennon - House System Morinus", 1940, 10, 9, 18, 30, "Liverpool", "GB", houses_system_identifier="M")
1133
+ morinus_house_chart = KerykeionChartSVG(morinus_house_subject)
1134
+ morinus_house_chart.makeSVG()
1135
+
1136
+ ## To check all the available house systems uncomment the following code:
1137
+ # from kerykeion.kr_types import HousesSystemIdentifier
1138
+ # from typing import get_args
1139
+ # for i in get_args(HousesSystemIdentifier):
1140
+ # alternatives_house_subject = AstrologicalSubject(f"John Lennon - House System {i}", 1940, 10, 9, 18, 30, "Liverpool", "GB", houses_system=i)
1141
+ # alternatives_house_chart = KerykeionChartSVG(alternatives_house_subject)
1142
+ # alternatives_house_chart.makeSVG()
1143
+
1144
+ # With True Geocentric Perspective
1145
+ true_geocentric_subject = AstrologicalSubject("John Lennon - True Geocentric", 1940, 10, 9, 18, 30, "Liverpool", "GB", perspective_type="True Geocentric")
1146
+ true_geocentric_chart = KerykeionChartSVG(true_geocentric_subject)
1147
+ true_geocentric_chart.makeSVG()
1148
+
1149
+ # With Heliocentric Perspective
1150
+ heliocentric_subject = AstrologicalSubject("John Lennon - Heliocentric", 1940, 10, 9, 18, 30, "Liverpool", "GB", perspective_type="Heliocentric")
1151
+ heliocentric_chart = KerykeionChartSVG(heliocentric_subject)
1152
+ heliocentric_chart.makeSVG()
1153
+
1154
+ # With Topocentric Perspective
1155
+ topocentric_subject = AstrologicalSubject("John Lennon - Topocentric", 1940, 10, 9, 18, 30, "Liverpool", "GB", perspective_type="Topocentric")
1156
+ topocentric_chart = KerykeionChartSVG(topocentric_subject)
1157
+ topocentric_chart.makeSVG()