Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -9,14 +9,14 @@ from timezonefinder import TimezoneFinder
|
|
9 |
import pytz
|
10 |
import swisseph as swe
|
11 |
|
12 |
-
#
|
13 |
-
swe.set_ephe_path(None)
|
14 |
|
15 |
# Russian translations for planets
|
16 |
PLANET_RU = {
|
17 |
-
'Sun': 'Солнце', 'Moon': 'Луна', 'Mercury': 'Меркурий',
|
18 |
-
'Venus': 'Венера', 'Mars': 'Марс',
|
19 |
-
'Saturn': 'Сатурн'
|
20 |
}
|
21 |
|
22 |
# Planet symbols for plotting
|
@@ -32,33 +32,23 @@ ZODIAC_SIGNS = [
|
|
32 |
]
|
33 |
|
34 |
def parse_query(query):
|
35 |
-
"""
|
36 |
-
Parse the query into date, time, and location.
|
37 |
-
Args:
|
38 |
-
query: String in format "PLadder YYYY-MM-DD HH:MM Location"
|
39 |
-
Returns:
|
40 |
-
tuple: (datetime, location, error_message)
|
41 |
-
"""
|
42 |
if not query.startswith("PLadder "):
|
43 |
return None, None, "Query must start with 'PLadder'"
|
44 |
|
45 |
try:
|
46 |
-
|
47 |
-
|
|
|
|
|
|
|
48 |
dt = parser.parse(f"{date_str} {time_str}")
|
49 |
return dt, location, None
|
50 |
except ValueError as e:
|
51 |
return None, None, f"Invalid format: {str(e)}"
|
52 |
|
53 |
def get_utc_time(dt, location):
|
54 |
-
"""
|
55 |
-
Convert local time to UTC using location's time zone.
|
56 |
-
Args:
|
57 |
-
dt: Local datetime
|
58 |
-
location: String location
|
59 |
-
Returns:
|
60 |
-
tuple: (utc_dt, lat, lon, error_message)
|
61 |
-
"""
|
62 |
geolocator = Nominatim(user_agent="pladder_app")
|
63 |
try:
|
64 |
loc = geolocator.geocode(location, timeout=10)
|
@@ -74,25 +64,17 @@ def get_utc_time(dt, location):
|
|
74 |
local_dt = tz.localize(dt)
|
75 |
utc_dt = local_dt.astimezone(pytz.UTC)
|
76 |
return utc_dt, lat, lon, None
|
77 |
-
|
78 |
except Exception as e:
|
79 |
-
return None, None, None, f"
|
80 |
|
81 |
def format_coords(lat, lon):
|
82 |
-
"""
|
83 |
-
Format coordinates as degrees, minutes, seconds.
|
84 |
-
Args:
|
85 |
-
lat: Latitude in degrees
|
86 |
-
lon: Longitude in degrees
|
87 |
-
Returns:
|
88 |
-
str: Formatted coordinates (e.g., "12°34'56" N, 98°45'32" E")
|
89 |
-
"""
|
90 |
def dms(value, pos_dir, neg_dir):
|
91 |
direction = pos_dir if value >= 0 else neg_dir
|
92 |
abs_value = abs(value)
|
93 |
degrees = int(abs_value)
|
94 |
minutes = int((abs_value - degrees) * 60)
|
95 |
-
seconds = round(((abs_value - degrees) * 60 - minutes) * 60)
|
96 |
|
97 |
# Handle rounding overflow
|
98 |
if seconds >= 60:
|
@@ -109,34 +91,26 @@ def format_coords(lat, lon):
|
|
109 |
def lon_to_sign(lon_deg):
|
110 |
"""
|
111 |
Convert ecliptic longitude to zodiac sign with position.
|
112 |
-
|
113 |
-
lon_deg: Longitude in degrees (0-360)
|
114 |
-
Returns:
|
115 |
-
str: Formatted sign and position (e.g., "Лев 12°34'")
|
116 |
"""
|
117 |
sign_idx = int(lon_deg // 30)
|
118 |
degrees_in_sign = lon_deg % 30
|
119 |
degrees = int(degrees_in_sign)
|
120 |
minutes = int((degrees_in_sign - degrees) * 60)
|
|
|
121 |
|
122 |
-
# Handle rounding
|
|
|
|
|
|
|
123 |
if minutes >= 60:
|
124 |
minutes -= 60
|
125 |
degrees += 1
|
126 |
|
127 |
-
return f"{ZODIAC_SIGNS[sign_idx]} {degrees}°{minutes:02}'"
|
128 |
|
129 |
def PLadder_ZSizes(utc_dt, lat, lon):
|
130 |
-
"""
|
131 |
-
Calculate Planetary Ladder and Zone Sizes using Swiss Ephemeris.
|
132 |
-
Args:
|
133 |
-
utc_dt: UTC datetime
|
134 |
-
lat: Latitude in degrees
|
135 |
-
lon: Longitude in degrees
|
136 |
-
Returns:
|
137 |
-
dict: Contains PLadder, ZSizes, and longitudes
|
138 |
-
"""
|
139 |
-
# Validate date range
|
140 |
if not -13000 <= utc_dt.year <= 17000:
|
141 |
return {"error": "Date out of supported range (-13,000–17,000 CE)"}
|
142 |
|
@@ -147,7 +121,7 @@ def PLadder_ZSizes(utc_dt, lat, lon):
|
|
147 |
'Jupiter': swe.JUPITER, 'Saturn': swe.SATURN
|
148 |
}
|
149 |
|
150 |
-
# Calculate Julian Day
|
151 |
jd_utc = swe.julday(
|
152 |
utc_dt.year, utc_dt.month, utc_dt.day,
|
153 |
utc_dt.hour + utc_dt.minute/60 + utc_dt.second/3600
|
@@ -190,7 +164,7 @@ def PLadder_ZSizes(utc_dt, lat, lon):
|
|
190 |
else:
|
191 |
X = 5
|
192 |
|
193 |
-
# Format size
|
194 |
d = int(size)
|
195 |
m = int((size - d) * 60)
|
196 |
s = int(round(((size - d) * 60 - m) * 60))
|
@@ -217,29 +191,23 @@ def PLadder_ZSizes(utc_dt, lat, lon):
|
|
217 |
return {
|
218 |
'PLadder': PLadder,
|
219 |
'ZSizes': ZSizes,
|
220 |
-
'longitudes':
|
221 |
}
|
222 |
|
223 |
def plot_pladder(PLadder):
|
224 |
-
"""
|
225 |
-
|
226 |
-
Args:
|
227 |
-
PLadder: Ordered list of planets
|
228 |
-
Returns:
|
229 |
-
matplotlib Figure
|
230 |
-
"""
|
231 |
-
fig, ax = plt.subplots(figsize=(8, 8))
|
232 |
|
233 |
-
# Draw main triangle
|
234 |
ax.plot([0, 1.5, 3, 0], [0, 3, 0, 0], 'k-', linewidth=2)
|
235 |
|
236 |
-
# Draw horizontal divisions
|
237 |
-
ax.plot([0.5, 2.5], [1, 1], 'k--'
|
238 |
-
ax.plot([1, 2], [2, 2], 'k--'
|
239 |
|
240 |
-
#
|
241 |
symbol_positions = [
|
242 |
-
(-0.2, 0.2), (0.3, 1.2), (0.8, 2.2),
|
243 |
(1.5, 3.2), (2.2, 2.2), (2.7, 1.2), (3.2, 0.2)
|
244 |
]
|
245 |
|
@@ -247,25 +215,18 @@ def plot_pladder(PLadder):
|
|
247 |
for (x, y), planet in zip(symbol_positions, PLadder):
|
248 |
ax.text(x, y, PLANET_SYMBOLS[planet],
|
249 |
ha='center', va='center',
|
250 |
-
fontsize=24
|
251 |
|
252 |
-
# Configure plot appearance
|
253 |
ax.set_xlim(-0.5, 3.5)
|
254 |
ax.set_ylim(-0.5, 3.5)
|
255 |
ax.set_aspect('equal')
|
256 |
ax.axis('off')
|
257 |
-
plt.tight_layout()
|
258 |
|
259 |
return fig
|
260 |
|
261 |
def chat_interface(query):
|
262 |
-
"""
|
263 |
-
Main processing function for the Gradio interface.
|
264 |
-
Args:
|
265 |
-
query: User input string
|
266 |
-
Returns:
|
267 |
-
tuple: (text_response, image)
|
268 |
-
"""
|
269 |
# Parse input
|
270 |
dt, location, error = parse_query(query)
|
271 |
if error:
|
@@ -286,7 +247,7 @@ def chat_interface(query):
|
|
286 |
ZSizes = result["ZSizes"]
|
287 |
longitudes = result["longitudes"]
|
288 |
|
289 |
-
# Generate planet list text
|
290 |
planet_list = "\n".join(
|
291 |
f"{PLANET_RU[p]}: {lon_to_sign(longitudes[p])}"
|
292 |
for p in PLadder
|
@@ -301,7 +262,7 @@ def chat_interface(query):
|
|
301 |
# Generate coordinates text
|
302 |
coords_text = format_coords(lat, lon)
|
303 |
|
304 |
-
# Create visualization
|
305 |
fig = plot_pladder(PLadder)
|
306 |
buf = BytesIO()
|
307 |
fig.savefig(buf, format='png', dpi=120, bbox_inches='tight')
|
|
|
9 |
import pytz
|
10 |
import swisseph as swe
|
11 |
|
12 |
+
# Initialize Swiss Ephemeris
|
13 |
+
swe.set_ephe_path(None)
|
14 |
|
15 |
# Russian translations for planets
|
16 |
PLANET_RU = {
|
17 |
+
'Sun': 'Солнце', 'Moon': 'Луна', 'Mercury': 'Меркурий',
|
18 |
+
'Venus': 'Венера', 'Mars': 'Марс',
|
19 |
+
'Jupiter': 'Юпитер', 'Saturn': 'Сатурн'
|
20 |
}
|
21 |
|
22 |
# Planet symbols for plotting
|
|
|
32 |
]
|
33 |
|
34 |
def parse_query(query):
|
35 |
+
"""Parse the query into date, time, and location."""
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
if not query.startswith("PLadder "):
|
37 |
return None, None, "Query must start with 'PLadder'"
|
38 |
|
39 |
try:
|
40 |
+
parts = query.split(maxsplit=3)
|
41 |
+
if len(parts) < 4:
|
42 |
+
return None, None, "Incomplete query (need date, time, and location)"
|
43 |
+
|
44 |
+
_, date_str, time_str, location = parts
|
45 |
dt = parser.parse(f"{date_str} {time_str}")
|
46 |
return dt, location, None
|
47 |
except ValueError as e:
|
48 |
return None, None, f"Invalid format: {str(e)}"
|
49 |
|
50 |
def get_utc_time(dt, location):
|
51 |
+
"""Convert local time to UTC using location's time zone."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
geolocator = Nominatim(user_agent="pladder_app")
|
53 |
try:
|
54 |
loc = geolocator.geocode(location, timeout=10)
|
|
|
64 |
local_dt = tz.localize(dt)
|
65 |
utc_dt = local_dt.astimezone(pytz.UTC)
|
66 |
return utc_dt, lat, lon, None
|
|
|
67 |
except Exception as e:
|
68 |
+
return None, None, None, f"Error: {str(e)}"
|
69 |
|
70 |
def format_coords(lat, lon):
|
71 |
+
"""Format coordinates as degrees, minutes, seconds."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
def dms(value, pos_dir, neg_dir):
|
73 |
direction = pos_dir if value >= 0 else neg_dir
|
74 |
abs_value = abs(value)
|
75 |
degrees = int(abs_value)
|
76 |
minutes = int((abs_value - degrees) * 60)
|
77 |
+
seconds = int(round(((abs_value - degrees) * 60 - minutes) * 60)
|
78 |
|
79 |
# Handle rounding overflow
|
80 |
if seconds >= 60:
|
|
|
91 |
def lon_to_sign(lon_deg):
|
92 |
"""
|
93 |
Convert ecliptic longitude to zodiac sign with position.
|
94 |
+
Now includes seconds in the output.
|
|
|
|
|
|
|
95 |
"""
|
96 |
sign_idx = int(lon_deg // 30)
|
97 |
degrees_in_sign = lon_deg % 30
|
98 |
degrees = int(degrees_in_sign)
|
99 |
minutes = int((degrees_in_sign - degrees) * 60)
|
100 |
+
seconds = int(round(((degrees_in_sign - degrees) * 60 - minutes) * 60)
|
101 |
|
102 |
+
# Handle rounding overflow
|
103 |
+
if seconds >= 60:
|
104 |
+
seconds -= 60
|
105 |
+
minutes += 1
|
106 |
if minutes >= 60:
|
107 |
minutes -= 60
|
108 |
degrees += 1
|
109 |
|
110 |
+
return f"{ZODIAC_SIGNS[sign_idx]} {degrees}°{minutes:02}'{seconds:02}\""
|
111 |
|
112 |
def PLadder_ZSizes(utc_dt, lat, lon):
|
113 |
+
"""Calculate Planetary Ladder and Zone Sizes using Swiss Ephemeris."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
if not -13000 <= utc_dt.year <= 17000:
|
115 |
return {"error": "Date out of supported range (-13,000–17,000 CE)"}
|
116 |
|
|
|
121 |
'Jupiter': swe.JUPITER, 'Saturn': swe.SATURN
|
122 |
}
|
123 |
|
124 |
+
# Calculate Julian Day
|
125 |
jd_utc = swe.julday(
|
126 |
utc_dt.year, utc_dt.month, utc_dt.day,
|
127 |
utc_dt.hour + utc_dt.minute/60 + utc_dt.second/3600
|
|
|
164 |
else:
|
165 |
X = 5
|
166 |
|
167 |
+
# Format size with seconds
|
168 |
d = int(size)
|
169 |
m = int((size - d) * 60)
|
170 |
s = int(round(((size - d) * 60 - m) * 60))
|
|
|
191 |
return {
|
192 |
'PLadder': PLadder,
|
193 |
'ZSizes': ZSizes,
|
194 |
+
'longitudes': longitudes # Raw degrees for calculations
|
195 |
}
|
196 |
|
197 |
def plot_pladder(PLadder):
|
198 |
+
"""Generate the original version of the planetary ladder visualization."""
|
199 |
+
fig, ax = plt.subplots(figsize=(6, 6))
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
|
201 |
+
# Draw the main triangle
|
202 |
ax.plot([0, 1.5, 3, 0], [0, 3, 0, 0], 'k-', linewidth=2)
|
203 |
|
204 |
+
# Draw horizontal divisions (original style)
|
205 |
+
ax.plot([0.5, 2.5], [1, 1], 'k--')
|
206 |
+
ax.plot([1, 2], [2, 2], 'k--')
|
207 |
|
208 |
+
# Original planet symbol positions
|
209 |
symbol_positions = [
|
210 |
+
(-0.2, 0.2), (0.3, 1.2), (0.8, 2.2),
|
211 |
(1.5, 3.2), (2.2, 2.2), (2.7, 1.2), (3.2, 0.2)
|
212 |
]
|
213 |
|
|
|
215 |
for (x, y), planet in zip(symbol_positions, PLadder):
|
216 |
ax.text(x, y, PLANET_SYMBOLS[planet],
|
217 |
ha='center', va='center',
|
218 |
+
fontsize=24)
|
219 |
|
220 |
+
# Configure plot appearance (original style)
|
221 |
ax.set_xlim(-0.5, 3.5)
|
222 |
ax.set_ylim(-0.5, 3.5)
|
223 |
ax.set_aspect('equal')
|
224 |
ax.axis('off')
|
|
|
225 |
|
226 |
return fig
|
227 |
|
228 |
def chat_interface(query):
|
229 |
+
"""Process the user query and return text and plot."""
|
|
|
|
|
|
|
|
|
|
|
|
|
230 |
# Parse input
|
231 |
dt, location, error = parse_query(query)
|
232 |
if error:
|
|
|
247 |
ZSizes = result["ZSizes"]
|
248 |
longitudes = result["longitudes"]
|
249 |
|
250 |
+
# Generate planet list text with full DMS
|
251 |
planet_list = "\n".join(
|
252 |
f"{PLANET_RU[p]}: {lon_to_sign(longitudes[p])}"
|
253 |
for p in PLadder
|
|
|
262 |
# Generate coordinates text
|
263 |
coords_text = format_coords(lat, lon)
|
264 |
|
265 |
+
# Create visualization (original style)
|
266 |
fig = plot_pladder(PLadder)
|
267 |
buf = BytesIO()
|
268 |
fig.savefig(buf, format='png', dpi=120, bbox_inches='tight')
|