libaditya 0.3.4__py2.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.
Files changed (131) hide show
  1. libaditya/__init__.py +46 -0
  2. libaditya/_version.py +29 -0
  3. libaditya/calc/__init__.py +9 -0
  4. libaditya/calc/api.py +30 -0
  5. libaditya/calc/avasthas.py +346 -0
  6. libaditya/calc/docs/jaimini-get-arch.md +95 -0
  7. libaditya/calc/hellenistic.py +37 -0
  8. libaditya/calc/jaimini.py +335 -0
  9. libaditya/calc/jaimini_get.py +240 -0
  10. libaditya/calc/kala.py +73 -0
  11. libaditya/calc/panchanga.py +422 -0
  12. libaditya/calc/rashi.py +612 -0
  13. libaditya/calc/returns.py +50 -0
  14. libaditya/calc/swe_functions.py +118 -0
  15. libaditya/calc/varga.py +287 -0
  16. libaditya/calc/vimshottari.py +431 -0
  17. libaditya/cards/__init__.py +5 -0
  18. libaditya/cards/card.py +90 -0
  19. libaditya/cards/cards_constants.py +115 -0
  20. libaditya/cards/cards_of_truth.py +323 -0
  21. libaditya/cards/cot.py +142 -0
  22. libaditya/cards/deck.py +52 -0
  23. libaditya/charts/__init__.py +6 -0
  24. libaditya/charts/api.py +36 -0
  25. libaditya/charts/bodygraph.py +80 -0
  26. libaditya/charts/chart.py +286 -0
  27. libaditya/charts/jaimini.py +83 -0
  28. libaditya/charts/tajika.py +36 -0
  29. libaditya/constants.py +1776 -0
  30. libaditya/draw/__init__.py +20 -0
  31. libaditya/draw/draw_bodygraph.py +489 -0
  32. libaditya/draw/draw_sbc.py +712 -0
  33. libaditya/draw/themes/default-theme.hd +53 -0
  34. libaditya/ephe/ast0/se00010s.se1 +0 -0
  35. libaditya/ephe/seas_00.se1 +0 -0
  36. libaditya/ephe/seas_06.se1 +0 -0
  37. libaditya/ephe/seas_12.se1 +0 -0
  38. libaditya/ephe/seas_18.se1 +0 -0
  39. libaditya/ephe/seas_24.se1 +0 -0
  40. libaditya/ephe/seas_30.se1 +0 -0
  41. libaditya/ephe/seas_36.se1 +0 -0
  42. libaditya/ephe/seas_42.se1 +0 -0
  43. libaditya/ephe/seas_48.se1 +0 -0
  44. libaditya/ephe/seasm06.se1 +0 -0
  45. libaditya/ephe/seasm12.se1 +0 -0
  46. libaditya/ephe/seasm18.se1 +0 -0
  47. libaditya/ephe/seasm24.se1 +0 -0
  48. libaditya/ephe/seasm30.se1 +0 -0
  49. libaditya/ephe/seasm36.se1 +0 -0
  50. libaditya/ephe/seasm42.se1 +0 -0
  51. libaditya/ephe/seasm48.se1 +0 -0
  52. libaditya/ephe/seasm54.se1 +0 -0
  53. libaditya/ephe/sefstars.txt +4245 -0
  54. libaditya/ephe/semo_00.se1 +0 -0
  55. libaditya/ephe/semo_06.se1 +0 -0
  56. libaditya/ephe/semo_12.se1 +0 -0
  57. libaditya/ephe/semo_18.se1 +0 -0
  58. libaditya/ephe/semo_24.se1 +0 -0
  59. libaditya/ephe/semo_30.se1 +0 -0
  60. libaditya/ephe/semo_36.se1 +0 -0
  61. libaditya/ephe/semo_42.se1 +0 -0
  62. libaditya/ephe/semo_48.se1 +0 -0
  63. libaditya/ephe/semom06.se1 +0 -0
  64. libaditya/ephe/semom12.se1 +0 -0
  65. libaditya/ephe/semom18.se1 +0 -0
  66. libaditya/ephe/semom24.se1 +0 -0
  67. libaditya/ephe/semom30.se1 +0 -0
  68. libaditya/ephe/semom36.se1 +0 -0
  69. libaditya/ephe/semom42.se1 +0 -0
  70. libaditya/ephe/semom48.se1 +0 -0
  71. libaditya/ephe/semom54.se1 +0 -0
  72. libaditya/ephe/sepl_00.se1 +0 -0
  73. libaditya/ephe/sepl_06.se1 +0 -0
  74. libaditya/ephe/sepl_12.se1 +0 -0
  75. libaditya/ephe/sepl_18.se1 +0 -0
  76. libaditya/ephe/sepl_24.se1 +0 -0
  77. libaditya/ephe/sepl_30.se1 +0 -0
  78. libaditya/ephe/sepl_36.se1 +0 -0
  79. libaditya/ephe/sepl_42.se1 +0 -0
  80. libaditya/ephe/sepl_48.se1 +0 -0
  81. libaditya/ephe/seplm06.se1 +0 -0
  82. libaditya/ephe/seplm12.se1 +0 -0
  83. libaditya/ephe/seplm18.se1 +0 -0
  84. libaditya/ephe/seplm24.se1 +0 -0
  85. libaditya/ephe/seplm30.se1 +0 -0
  86. libaditya/ephe/seplm36.se1 +0 -0
  87. libaditya/ephe/seplm42.se1 +0 -0
  88. libaditya/ephe/seplm48.se1 +0 -0
  89. libaditya/ephe/seplm54.se1 +0 -0
  90. libaditya/hd/__init__.py +8 -0
  91. libaditya/hd/calc.py +55 -0
  92. libaditya/hd/constants.py +127 -0
  93. libaditya/hd/longitude.py +271 -0
  94. libaditya/objects/__init__.py +19 -0
  95. libaditya/objects/celestial_object.py +206 -0
  96. libaditya/objects/context.py +133 -0
  97. libaditya/objects/cusps.py +259 -0
  98. libaditya/objects/julian_day.py +265 -0
  99. libaditya/objects/location.py +167 -0
  100. libaditya/objects/longitude.py +987 -0
  101. libaditya/objects/nakshatras.py +285 -0
  102. libaditya/objects/planets.py +2059 -0
  103. libaditya/objects/shadbala.py +398 -0
  104. libaditya/objects/signs.py +690 -0
  105. libaditya/objects/swe_functions.py +75 -0
  106. libaditya/print_functions.py +548 -0
  107. libaditya/read.py +665 -0
  108. libaditya/stars/__init__.py +4 -0
  109. libaditya/stars/fixed_star.py +216 -0
  110. libaditya/stars/make_swe_stars.py +407 -0
  111. libaditya/stars/star-sign-boundaries +44 -0
  112. libaditya/stars/stellarium/__init__.py +6 -0
  113. libaditya/stars/stellarium/remote_control/__init__.py +22 -0
  114. libaditya/stars/stellarium/remote_control/location.py +82 -0
  115. libaditya/stars/stellarium/remote_control/locationsearch.py +35 -0
  116. libaditya/stars/stellarium/remote_control/main.py +165 -0
  117. libaditya/stars/stellarium/remote_control/objects.py +63 -0
  118. libaditya/stars/stellarium/remote_control/scripts.py +81 -0
  119. libaditya/stars/stellarium/remote_control/simbad.py +20 -0
  120. libaditya/stars/stellarium/remote_control/stelaction.py +31 -0
  121. libaditya/stars/stellarium/remote_control/stelproperty.py +30 -0
  122. libaditya/stars/stellarium/remote_control/view.py +85 -0
  123. libaditya/stars/stellarium/stellarium.py +92 -0
  124. libaditya/stars/the_stars.py +522 -0
  125. libaditya/stars/utilities.py +539 -0
  126. libaditya/utils.py +431 -0
  127. libaditya/write.py +67 -0
  128. libaditya-0.3.4.dist-info/METADATA +588 -0
  129. libaditya-0.3.4.dist-info/RECORD +131 -0
  130. libaditya-0.3.4.dist-info/WHEEL +5 -0
  131. libaditya-0.3.4.dist-info/licenses/LICENSE +661 -0
libaditya/utils.py ADDED
@@ -0,0 +1,431 @@
1
+ # This file is part of libaditya.
2
+ #
3
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
4
+ #
5
+ # libaditya is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # libaditya is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import swisseph as swe
19
+ import time as tmod
20
+ import os
21
+
22
+ from libaditya import constants as const
23
+
24
+ def dms2dec(dms):
25
+ """
26
+ dms is a tuple (hour,minutes,seconds) that wants to be turned into a float
27
+ """
28
+ return dms[0] + (dms[1] / 60) + (dms[2] / 3600)
29
+
30
+ def sign_degree_to_longitude(sd: float, context):
31
+ """
32
+ sd is a float of the 10.28, i.e., 10th sign, 28th degree
33
+ now change to an ecliptic longitude based on which Circle we are using
34
+ """
35
+ from libaditya.objects import Circle
36
+ sign=int(sd[0])
37
+ degrees=float(sd[1])
38
+ if context.circle == Circle.ZODIAC:
39
+ return ((sign-1)*30) + degrees
40
+ else:
41
+ return (((sign-2)%12)*30) + degrees
42
+
43
+
44
+ def signize(long, toround, names):
45
+ """
46
+ return a string with 360degree longitude long given with
47
+ long (sign), with long being in the sign
48
+ signs contains the signs to be used, which might be adityas
49
+ """
50
+ index = int(
51
+ (long % 360) / 30
52
+ ) # mod 360 in case long=360...but it probably wouldnt with swe, right?
53
+ if toround[0]:
54
+ inlong = round(long % 30, toround[1])
55
+ else:
56
+ inlong = long % 30
57
+ return f"{dec2dmsstr(inlong)} {names[index]}"
58
+
59
+
60
+ def intize_date(date):
61
+ """
62
+ take a string 'MM/DD/YYYY'
63
+ and return a tuple of int (mm,dd,yyyy)
64
+ """
65
+ date = date.split("/")
66
+ year = int(date[2])
67
+ month = int(date[0])
68
+ day = int(date[1])
69
+
70
+ return (month, day, year)
71
+
72
+ def even(n):
73
+ return n%2 == 0
74
+
75
+ def odd(n):
76
+ return n%2 == 1
77
+
78
+ def intize_time(time):
79
+ """
80
+ take a string 'HH:MM:SS'
81
+ and return a float of that time
82
+ """
83
+ time = time.split(":")
84
+ # if len == 2, HH:MM, otherwise, HH:MM:SS
85
+ if len(time) == 2:
86
+ return int(time[0]) + int(time[1]) / 60
87
+ else:
88
+ return int(time[0]) + int(time[1]) / 60 + int(time[2]) / 3600
89
+
90
+
91
+ def tmod_to_jd(now):
92
+ """
93
+ time a struct from the time module
94
+ and convert it to julian day float
95
+ """
96
+ return swe.julday(
97
+ now.tm_year,
98
+ now.tm_mon,
99
+ now.tm_mday,
100
+ now.tm_hour + now.tm_min / 60 + now.tm_sec / 3600,
101
+ )
102
+
103
+
104
+ def date2str(date):
105
+ """date is a revjul-tuple (year,month,day,hour); return a string 'month/day/year'"""
106
+ return f"{date[1]:02d}/{date[2]:02d}/{date[0]:02d}"
107
+
108
+
109
+ def time2str(time):
110
+ """time is a dec2dms tupel (deg,min,sec); returns a string 'HH:MM:SS'"""
111
+ return f"{str(int(time[0])).zfill(2)}:{str(int(time[1])).zfill(2)}:{str(int(time[2])).zfill(2)}"
112
+
113
+ def mktimezone(offset,timezone="UTC"):
114
+ sign = ""
115
+ appendix = ""
116
+ if offset > 0:
117
+ sign = "+"
118
+ if offset == 0 or timezone != "UTC":
119
+ appendix = ""
120
+ else:
121
+ appendix = sign + f"{round(offset, 2)}"
122
+ return timezone + appendix
123
+
124
+ def mktab(n):
125
+ tab = ""
126
+ for i in range(0, n):
127
+ tab += "\t"
128
+ return tab
129
+
130
+
131
+ def dec2dms(dd):
132
+ """dd is a float that wants to be turned into degs,mins,secs; returns (deg,min,sec)"""
133
+ dd = abs(dd)
134
+ minutes, seconds = divmod(dd * 3600, 60)
135
+ degrees, minutes = divmod(minutes, 60)
136
+ return (degrees, minutes, seconds)
137
+
138
+
139
+ def dec2dmsstr(dd):
140
+ """dd is a float that wants to be turned into degs,mins,secs; returns (deg,min,sec)"""
141
+ dd = abs(dd)
142
+ minutes, seconds = divmod(dd * 3600, 60)
143
+ degrees, minutes = divmod(minutes, 60)
144
+ return f"{int(degrees):02d}:{int(minutes):02d}:{int(seconds):02d}"
145
+
146
+
147
+
148
+ def dec2ymd(age: float) -> str:
149
+ """
150
+ take a floating point age (=number of years) and return a string
151
+ "x years, y months, z days, a hours, b minutes, c seconds"
152
+ the days isnt precise, since i didnt just now feel like
153
+ programming the details of that
154
+ if any of these are zero, it does not print that
155
+ """
156
+ # import pdb; pdb.set_trace()
157
+ sign = ""
158
+ if age < 0:
159
+ sign = "-"
160
+ age=-age
161
+ strlist=[]
162
+ # how many years is the integer part
163
+ years = int(age)
164
+ if not years == 0:
165
+ strlist.append(f"{years} years")
166
+ # get the decimal part of the float
167
+ rem = age % 1
168
+ # multiple by 12 to find numbers of months as a float
169
+ md = rem*12
170
+ # months is the decimal part
171
+ months = int(md)
172
+ if not months == 0:
173
+ strlist.append(f"{months} months")
174
+ rem = md % 1
175
+ # days are approximatebecause to be precise we need to find
176
+ # the precise monthetc. and i dont feel like it
177
+ dpart = rem*31
178
+ days = int(rem*31)
179
+ if not days == 0:
180
+ strlist.append(f"{days} days")
181
+ if years == 0 and months == 0:
182
+ # only compute hours and seconds if years and months are 0
183
+ rem = dpart%1
184
+ dpart = rem*24
185
+ hours = int(dpart)
186
+ if not hours == 0:
187
+ strlist.append(f"{hours} hours")
188
+ mpart = rem*60
189
+ minutes = int(mpart)
190
+ if not minutes == 0:
191
+ strlist.append(f"{minutes} minutes")
192
+ rem = mpart%1
193
+ seconds = int(rem*60)
194
+ if not seconds == 0:
195
+ strlist.append(f"{seconds} seconds")
196
+ if not strlist:
197
+ return "Less than one second"
198
+ strlist[0] = sign+strlist[0]
199
+ return ", ".join(strlist)
200
+
201
+ def mktab(n):
202
+ tab = ""
203
+ for i in range(0,n):
204
+ tab += "\t"
205
+ return tab
206
+
207
+ def mksub(n):
208
+ sub = ""
209
+ for i in range(0,n):
210
+ sub += "sub"
211
+ return sub
212
+
213
+ def construct_varga_row(cusp):
214
+ """
215
+ construct a row where every column
216
+ is "" except the one the corresponds to the sign
217
+ the planet/cusp is in, then print its in_sign_longitude there
218
+ e.g., if Sun is in Cancer, print nothing in the
219
+ first, second column, third column, print longitude in fourth column, nothing in fifth,etc.
220
+ """
221
+ ret = []
222
+
223
+ for n in range(0,12):
224
+ if n == cusp.sign_index():
225
+ ret.append(cusp.in_sign_longitude())
226
+ else:
227
+ ret.append("")
228
+ return ret
229
+
230
+ # make a string for dasha lords including subdashas
231
+ def mk_dasha_lord(dlist):
232
+ """
233
+ make a string of dashas lords
234
+ """
235
+ lordstr = ""
236
+ for lord in range(0,len(dlist)):
237
+ if lord == len(dlist)-1:
238
+ lordstr += const.vimshottari_dashas[dlist[lord]][0]
239
+ else:
240
+ lordstr += const.vimshottari_dashas[dlist[lord]][0] + "/"
241
+ return lordstr
242
+
243
+ DIGNITY_RANK = {"EX": 9, "MT": 8, "OH": 7, "GF": 6, "F": 5, "N": 4, "E": 3, "GE": 2, "DB": 1}
244
+
245
+ def compare_signs_dignities(sign1,sign2,dignities: [str]) -> int:
246
+ """
247
+ compare which sign has stronger dignities among its occupying planets
248
+ dignities is the list of planetary dignities returned by Planets.dignities()
249
+
250
+ if different planet counts, more planets wins
251
+ if same count, sort each sign's planets by dignity descending,
252
+ compare element by element; first difference determines the winner
253
+
254
+ 0 means equal
255
+ 1 means sign1 has stronger dignities
256
+ 2 means sign2 has stronger dignities
257
+ """
258
+ s1digs = sorted([DIGNITY_RANK.get(dignities[p.list_index()], 0) for p in sign1.karakas()], reverse=True)
259
+ s2digs = sorted([DIGNITY_RANK.get(dignities[p.list_index()], 0) for p in sign2.karakas()], reverse=True)
260
+ if not s1digs and not s2digs:
261
+ return 0
262
+ if len(s1digs) != len(s2digs):
263
+ return 1 if len(s1digs) > len(s2digs) else 2
264
+ for d1, d2 in zip(s1digs, s2digs):
265
+ if d1 > d2: return 1
266
+ if d1 < d2: return 2
267
+ return 0
268
+
269
+ def compare_planets_dignities(planet1,planet2) -> int:
270
+ """
271
+ compare the dignities of these two planets
272
+ 0 for equal
273
+ 1 for planet1 has higher dignity
274
+ 2 for planet2 has higher dignity
275
+ """
276
+ match planet1,planet2:
277
+ case "EX", "EX":
278
+ return 0
279
+ case "EX", _:
280
+ return 1
281
+ case _, "EX":
282
+ return 2
283
+ case "MT", "MT":
284
+ return 0
285
+ case "MT", _:
286
+ return 1
287
+ case _, "MT":
288
+ return 2
289
+ case "GF", "GF":
290
+ return 0
291
+ case "GF", _:
292
+ return 1
293
+ case _, "GF":
294
+ return 2
295
+ case "F", "F":
296
+ return 0
297
+ case "F", _:
298
+ return 1
299
+ case _, "F":
300
+ return 2
301
+ case "E", "E":
302
+ return 0
303
+ case "E", _:
304
+ return 1
305
+ case _, "E":
306
+ return 2
307
+ case "N", "N":
308
+ return 0
309
+ case "N", _:
310
+ return 1
311
+ case _, "N":
312
+ return 2
313
+ case "GE", "GE":
314
+ return 0
315
+ case "GE", _:
316
+ return 1
317
+ case _, "GE":
318
+ return 2
319
+ case "DB", "DB":
320
+ return 0
321
+ case "DB", _:
322
+ return 1
323
+ case _, "DB":
324
+ return 2
325
+
326
+
327
+
328
+ def compare_signs_modalities(sign1,sign2) -> int:
329
+ """
330
+ compare the modalities of sign1 and sign2, both Sign classes
331
+ return which is stronger
332
+ 0 means equal strength
333
+ 1 means sign1 is stronger
334
+ 2 means sign2 is stronger
335
+ """
336
+ if sign1.modality() == sign2.modality():
337
+ return 0
338
+ match (sign1.modality(),sign2.modality()):
339
+ case ("Dual", "Fixed"):
340
+ return 1
341
+ case ("Dual", "Moveable"):
342
+ return 1
343
+ case ("Fixed", "Dual"):
344
+ return 2
345
+ case ("Fixed", "Moveable"):
346
+ return 1
347
+ case ("Moveable", "Dual"):
348
+ return 2
349
+ case ("Moveable", "Fixed"):
350
+ return 2
351
+
352
+ def copy_collect_charts(root,outdir="all-charts"):
353
+ """
354
+ will copy all .chtk files in root and down to outdir/*.chtk
355
+ will give errors if run again because the file is already there, but it works
356
+ """
357
+ import subprocess
358
+ ret = []
359
+ for root, sub, files in os.walk(root):
360
+ for file in files:
361
+ this_path = os.path.join(root, file)
362
+ res = subprocess.call(["cp", f"{this_path}", f"{outdir}/{file}"])
363
+ if res != 0:
364
+ print("cp command failed")
365
+
366
+ return ret
367
+
368
+ def toJD(ls,context):
369
+ """
370
+ take a list ls of julianday floats
371
+ return a list of JulianDay classes
372
+ """
373
+ from libaditya.objects import JulianDay
374
+ return [JulianDay(jd_number,context.timeJD.utcoffset) for jd_number in ls]
375
+
376
+ def is_stellarium_id(swe_id):
377
+ """
378
+ find if swe_id is actually a stellarium id
379
+ """
380
+ if isinstance(swe_id, int):
381
+ return False
382
+ return "HIP" in swe_id or " " in swe_id
383
+
384
+
385
+ def set_swe_true_sidereal_ayanamsa():
386
+ """
387
+ this is used by calling TheStars.set_true_sidereal_hd_ayanamsa()
388
+ this sets the swe ayanamsa to the true sidereal ayanamsa, so that when we intialize
389
+ our stars they have the appropriate coordinates for what we are doing
390
+
391
+ define a custom ayanamsha
392
+ this is from the faq at masteringthezodiac.com
393
+ • Ayanamsa: User Defined SVP
394
+ • Fixed Sidereal Vernal Point: 31.2836
395
+ • Yearly Incremental SVP: 0.00
396
+ • Reference Year: 2000
397
+ reference year means January 1, 2000
398
+ Then choose the true sidereal-M (Midpoint) setting
399
+ """
400
+ swe.set_sid_mode(swe.SIDM_USER + swe.SIDBIT_USER_UT, 2451545.0, 31.2836)
401
+
402
+
403
+ def mkheader(obj):
404
+ header = ""
405
+ header += f"{obj.context.name}\n"
406
+ header += f"Varga {obj.context.amsha} {obj.varga_name()}\n"
407
+ header += f"{obj.sysflgstr} coordinates\n"
408
+ header += f"{const.circle_name(obj.context.circle)}\n"
409
+ header += f"House system {swe.house_name(obj.context.hsys.encode())}\n"
410
+ digplace = "rashi" if obj.context.rashi_temporary_friendships else "varga"
411
+ header += f"Dignities based on {digplace}\n"
412
+ header += f"{obj.context.rashi_aspects} rashi aspects\n"
413
+ if obj.context.sysflg == swe.FLG_SIDEREAL:
414
+ # for sidereal signs we actually use swisseph 36
415
+ # dhruva equatorial is only for nakshatras
416
+ if obj.context.ayanamsa == 98:
417
+ header += f"{const.ayanamsa_name(36)} ayanamsa for signs\n"
418
+ header += f"{const.ayanamsa_name(98)} ayanamsa for nakshatras\n"
419
+ else:
420
+ header += f"{const.ayanamsa_name(obj.context.ayanamsa)} ayanamsa\n"
421
+ elif obj.context.sysflg == (swe.FLG_SIDEREAL | swe.FLG_TOPOCTR):
422
+ if obj.context.ayanamsa == 98:
423
+ header += f"{const.ayanamsa_name(36)} ayanamsa for signs\n"
424
+ header += f"{const.ayanamsa_name(98)} ayanamsa for nakshatras\n"
425
+ header += f"{obj.context.location.placename()} ({obj.context.location.latitude()} lat, {obj.context.location.longitude()} long)\n"
426
+ header += f"{const.ayanamsa_name(obj.context.ayanamsa)} ayanamsa\n"
427
+ else:
428
+ header += f"{const.ayanamsa_name(obj.context.ayanamsa)} ayanamsa\n"
429
+ header += f"{obj.context.location.placename()} ({obj.context.location.latitude()} lat, {obj.context.location.longitude()} long)\n"
430
+ header += f"{obj.context.timeJD}\n"
431
+ return header
libaditya/write.py ADDED
@@ -0,0 +1,67 @@
1
+ # This file is part of libaditya.
2
+ #
3
+ # Copyright (c) 2025 Josh Harper <humanhaven@substack.com>
4
+ #
5
+ # libaditya is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # libaditya is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU Affero General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Affero General Public License
16
+ # along with libaditya. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ import toml
19
+
20
+ from libaditya.objects import JulianDay, Location
21
+ from libaditya import read
22
+
23
+ def new_chart_interactive(outfile=None):
24
+ """
25
+ take user input and write out a new chart
26
+
27
+ name: str (optional)
28
+ date: MM/DD/YYYY
29
+ hour: (HH:MM(:SS)) (UTC)
30
+ utcoffset: float
31
+ lat: N is positive
32
+ long: E is positive
33
+ three formats: 1) decimal
34
+ 2) DD:MM(:SS)
35
+ 3) 0DD(E/W)MM'SS(.SS)
36
+ 3) DD(N/S)MM'SS(.SS)
37
+ alt: float - meters
38
+ placename: str (optional)
39
+ """
40
+ name = input("Name: ")
41
+ date = input("Date (MM/DD/YYYY): ")
42
+ hour = input("Hour (HH:MM(:SS)) (UTC): ")
43
+ utcoffset = float(input("UTC offset: "))
44
+ # lat and long are floats
45
+ lat = read.parse_position_argument(input("Latitude (N is positive): "))
46
+ long = read.parse_position_argument(input("Longitude (E is positive): "))
47
+ alt = float(input("Altitude (meters): "))
48
+ placename = input("Placename (optional): ")
49
+
50
+ # name, placename, month, day, year, timedec, lat, long, utcoffset
51
+ month, day, year = read.intize_date(date)
52
+ timedec = read.intize_time(hour)
53
+ timeJD = JulianDay((year,month,day,timedec),utcoffset)
54
+ location = Location(lat, long, alt, placename, timeJD.mktimezone())
55
+ d=dict()
56
+ d["name"] = name.split(".")
57
+ d["timeJD"] = dict()
58
+ d["timeJD"]["jd"]=timeJD.jd_number()
59
+ d["timeJD"]["utcoffset"]=timeJD.utcoffset
60
+ d["location"]=location.__dict__
61
+
62
+ if outfile is not None:
63
+ # split in case they put extension on there
64
+ name=outfile.split(".")
65
+ with open(f"{name.lower().replace(' ','-')}.toml", "w") as fd:
66
+ toml.dump(d,fd)
67
+ return