SergeyO7 commited on
Commit
e67f755
·
verified ·
1 Parent(s): 99faed2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -116
app.py CHANGED
@@ -2,6 +2,7 @@ import gradio as gr
2
  import matplotlib.pyplot as plt
3
  from skyfield.api import load, Topos
4
  from datetime import datetime
 
5
  from io import BytesIO
6
  from PIL import Image
7
  from geopy.geocoders import Nominatim
@@ -20,67 +21,51 @@ planet_symbols = {
20
  'Mars': '♂', 'Jupiter': '♃', 'Saturn': '♄'
21
  }
22
 
23
- def parse_query(query_content):
24
- """Parse the query into UTC datetime, latitude, and longitude."""
25
- parts = query_content.split()
26
  if len(parts) < 3:
27
- return None, None, None, "Insufficient information in query."
28
-
29
- date_str = parts[0]
30
- time_str = parts[1]
31
- location_str = " ".join(parts[2:])
32
-
33
- # Parse local date and time
34
  try:
35
- local_dt = datetime.strptime(f"{date_str} {time_str}", "%Y-%m-%d %H:%M")
 
36
  except ValueError:
37
- return None, None, None, "Invalid date or time format."
38
-
39
- # Geocode location to get latitude and longitude
40
- geolocator = Nominatim(user_agent="pladder_app")
 
41
  try:
42
- location = geolocator.geocode(location_str)
43
- if location is None:
44
  return None, None, None, "Location not found."
45
- lat, lon = location.latitude, location.longitude
46
- except Exception as e:
47
- return None, None, None, f"Geocoding error: {str(e)}"
48
-
49
- # Determine time zone
50
- tf = TimezoneFinder()
51
- try:
52
  tz_str = tf.timezone_at(lng=lon, lat=lat)
53
- if tz_str is None:
54
  return None, None, None, "Time zone not found."
55
  tz = pytz.timezone(tz_str)
56
- except Exception as e:
57
- return None, None, None, f"Time zone determination error: {str(e)}"
58
-
59
- # Convert local time to UTC
60
- try:
61
- local_dt = tz.localize(local_dt)
62
  utc_dt = local_dt.astimezone(pytz.utc)
 
63
  except Exception as e:
64
- return None, None, None, f"Time conversion error: {str(e)}"
65
-
66
- return utc_dt, lat, lon, None
67
 
68
  def format_coords(lat, lon):
69
- """Format latitude and longitude as degrees, minutes, seconds."""
70
- def format_part(value, pos_dir, neg_dir):
71
- direction = pos_dir if value >= 0 else neg_dir
72
  value = abs(value)
73
- degrees = int(value)
74
- minutes = int((value - degrees) * 60)
75
- seconds = int(((value - degrees) * 60 - minutes) * 60)
76
- return f"{degrees}°{minutes:02d}'{seconds:02d}\" {direction}"
77
-
78
- lat_str = format_part(lat, "N", "S")
79
- lon_str = format_part(lon, "E", "W")
80
- return f"{lat_str}, {lon_str}"
81
 
82
  def lon_to_sign(lon):
83
- """Convert longitude to zodiac sign with degrees and minutes."""
84
  signs = ["Овен", "Телец", "Близнецы", "Рак", "Лев", "Дева",
85
  "Весы", "Скорпион", "Стрелец", "Козерог", "Водолей", "Рыбы"]
86
  sign_index = int(lon // 30)
@@ -90,67 +75,50 @@ def lon_to_sign(lon):
90
 
91
  def PLadder_ZSizes(utc_dt, lat, lon):
92
  """Calculate Planetary Ladder and Zone Sizes."""
93
- try:
94
- if not 1900 <= utc_dt.year <= 2050:
95
- return {"error": "Date out of range (1900–2050)."}
96
-
97
- planets = load('de421.bsp')
98
- earth = planets['earth']
99
- observer = earth + Topos(latitude_degrees=lat, longitude_degrees=lon)
100
-
101
- planet_objects = {
102
- 'Sun': planets['sun'], 'Moon': planets['moon'], 'Mercury': planets['mercury'],
103
- 'Venus': planets['venus'], 'Mars': planets['mars'],
104
- 'Jupiter': planets['jupiter barycenter'], 'Saturn': planets['saturn barycenter']
105
- }
106
-
107
- ts = load.timescale()
108
- t = ts.from_datetime(utc_dt)
109
-
110
- longitudes = {}
111
- for planet, obj in planet_objects.items():
112
- astrometric = observer.at(t).observe(obj)
113
- _, lon, _ = astrometric.apparent().ecliptic_latlon()
114
- longitudes[planet] = lon.degrees
115
-
116
- sorted_planets = sorted(longitudes.items(), key=lambda x: x[1])
117
- PLadder = [p for p, _ in sorted_planets]
118
- sorted_lons = [lon for _, lon in sorted_planets]
119
-
120
- zone_sizes = [sorted_lons[0]] + [sorted_lons[i+1] - sorted_lons[i] for i in range(6)] + [360 - sorted_lons[6]]
121
- bordering = [[PLadder[0]]] + [[PLadder[i-1], PLadder[i]] for i in range(1, 7)] + [[PLadder[6]]]
122
-
123
- ZSizes = []
124
- for i, size in enumerate(zone_sizes):
125
- bord = bordering[i]
126
- X = 7 if any(p in ['Sun', 'Moon'] for p in bord) else 6 if any(p in ['Mercury', 'Venus', 'Mars'] for p in bord) else 5
127
- classification = ('Swallowed' if size <= 1 else 'Tiny' if size <= X else 'Small' if size <= 40 else
128
- 'Ideal' if 50 <= size <= 52 else 'Normal' if size < 60 else 'Big')
129
- d, m = int(size), int((size - int(size)) * 60)
130
- ZSizes.append((f"{d}°{m}'", classification))
131
-
132
- return {'PLadder': PLadder, 'ZSizes': ZSizes, 'longitudes': longitudes}
133
-
134
- except Exception as e:
135
- return {"error": f"Calculation error: {str(e)}"}
136
 
137
  def plot_pladder(PLadder):
138
- """Plot the Planetary Ladder with adjusted symbol positions and horizontal lines."""
139
  fig, ax = plt.subplots()
140
- # Triangle boundaries
141
- ax.plot([0, 1.5, 3, 0], [0, 3, 0, 0], 'k-')
142
  # Horizontal lines within triangle
143
- ax.plot([0.5, 2.5], [1, 1], 'k--')
144
- ax.plot([1, 2], [2, 2], 'k--')
145
- # Planet symbol positions, offset ~0.2 units from triangle sides
146
  positions = [
147
- (0.575, 0.75), # Left side
148
- (0.95, 1.5),
149
- (1.325, 2.25),
150
- (1.5, 2.7), # Top
151
- (1.675, 2.25), # Right side
152
- (2.05, 1.5),
153
- (2.425, 0.75)
154
  ]
155
  for i, (x, y) in enumerate(positions):
156
  ax.text(x, y, planet_symbols[PLadder[i]], ha='center', va='center', fontsize=24)
@@ -164,35 +132,32 @@ def chat_interface(query):
164
  """Process the user query and return text and plot."""
165
  if not query.startswith("PLadder "):
166
  return "Query must start with 'PLadder' followed by date, time, and location.", None
167
-
168
  query_content = query.split(" ", 1)[1]
169
- utc_dt, lat, lon, error = parse_query(query_content)
 
 
 
170
  if error:
171
  return error, None
172
-
173
  result = PLadder_ZSizes(utc_dt, lat, lon)
174
  if "error" in result:
175
  return result["error"], None
176
-
177
  PLadder = result["PLadder"]
178
  ZSizes = result["ZSizes"]
179
  longitudes = result["longitudes"]
180
-
181
  planet_list = "\n".join([f"{planet_ru[p]}: {lon_to_sign(longitudes[p])}" for p in PLadder])
182
  zones_text = "\n".join([f"Zone {i+1}: {size} ({cls})" for i, (size, cls) in enumerate(ZSizes)])
183
  coords_text = format_coords(lat, lon)
184
-
185
  fig = plot_pladder(PLadder)
186
  buf = BytesIO()
187
  fig.savefig(buf, format='png', bbox_inches='tight')
188
  buf.seek(0)
189
  img = Image.open(buf)
190
  plt.close(fig)
191
-
192
  text = f"Planetary Ladder:\n{planet_list}\n\nZone Sizes:\n{zones_text}\n\nCoordinates: {coords_text}"
193
  return text, img
194
 
195
- # Gradio UI with a single query field
196
  with gr.Blocks() as interface:
197
  with gr.Row():
198
  with gr.Column(scale=2):
@@ -202,11 +167,6 @@ with gr.Blocks() as interface:
202
  with gr.Row():
203
  query_text = gr.Textbox(label="Query", placeholder="Example: PLadder 2023-10-10 12:00 New York")
204
  submit_button = gr.Button("Submit")
205
-
206
- submit_button.click(
207
- fn=chat_interface,
208
- inputs=query_text,
209
- outputs=[output_text, output_image]
210
- )
211
 
212
  interface.launch()
 
2
  import matplotlib.pyplot as plt
3
  from skyfield.api import load, Topos
4
  from datetime import datetime
5
+ from dateutil import parser
6
  from io import BytesIO
7
  from PIL import Image
8
  from geopy.geocoders import Nominatim
 
21
  'Mars': '♂', 'Jupiter': '♃', 'Saturn': '♄'
22
  }
23
 
24
+ def parse_query(query):
25
+ """Parse the query into date, time, and location."""
26
+ parts = query.split()
27
  if len(parts) < 3:
28
+ return None, None, "Insufficient information."
29
+ date_str, time_str, *location_parts = parts
30
+ location = " ".join(location_parts)
 
 
 
 
31
  try:
32
+ dt = parser.parse(f"{date_str} {time_str}")
33
+ return dt, location, None
34
  except ValueError:
35
+ return None, None, "Invalid date or time format."
36
+
37
+ def get_utc_time(dt, location):
38
+ """Convert local time to UTC using location's time zone."""
39
+ geolocator = Nominatim(user_agent="pladder")
40
  try:
41
+ loc = geolocator.geocode(location)
42
+ if not loc:
43
  return None, None, None, "Location not found."
44
+ lat, lon = loc.latitude, loc.longitude
45
+ tf = TimezoneFinder()
 
 
 
 
 
46
  tz_str = tf.timezone_at(lng=lon, lat=lat)
47
+ if not tz_str:
48
  return None, None, None, "Time zone not found."
49
  tz = pytz.timezone(tz_str)
50
+ local_dt = tz.localize(dt)
 
 
 
 
 
51
  utc_dt = local_dt.astimezone(pytz.utc)
52
+ return utc_dt, lat, lon, None
53
  except Exception as e:
54
+ return None, None, None, f"Error: {str(e)}"
 
 
55
 
56
  def format_coords(lat, lon):
57
+ """Format coordinates as degrees, minutes, seconds."""
58
+ def dms(value, pos, neg):
59
+ dir = pos if value >= 0 else neg
60
  value = abs(value)
61
+ d = int(value)
62
+ m = int((value - d) * 60)
63
+ s = int(((value - d) * 60 - m) * 60)
64
+ return f"{d}°{m:02}'{s:02}\" {dir}"
65
+ return f"{dms(lat, 'N', 'S')}, {dms(lon, 'E', 'W')}"
 
 
 
66
 
67
  def lon_to_sign(lon):
68
+ """Convert longitude to zodiac sign format."""
69
  signs = ["Овен", "Телец", "Близнецы", "Рак", "Лев", "Дева",
70
  "Весы", "Скорпион", "Стрелец", "Козерог", "Водолей", "Рыбы"]
71
  sign_index = int(lon // 30)
 
75
 
76
  def PLadder_ZSizes(utc_dt, lat, lon):
77
  """Calculate Planetary Ladder and Zone Sizes."""
78
+ if not 1900 <= utc_dt.year <= 2050:
79
+ return {"error": "Date out of range (1900–2050)."}
80
+ planets = load('de421.bsp')
81
+ earth = planets['earth']
82
+ observer = earth + Topos(latitude_degrees=lat, longitude_degrees=lon)
83
+ planet_objects = {
84
+ 'Sun': planets['sun'], 'Moon': planets['moon'], 'Mercury': planets['mercury'],
85
+ 'Venus': planets['venus'], 'Mars': planets['mars'],
86
+ 'Jupiter': planets['jupiter barycenter'], 'Saturn': planets['saturn barycenter']
87
+ }
88
+ ts = load.timescale()
89
+ t = ts.from_datetime(utc_dt)
90
+ longitudes = {}
91
+ for planet, obj in planet_objects.items():
92
+ astrometric = observer.at(t).observe(obj)
93
+ _, lon, _ = astrometric.apparent().ecliptic_latlon()
94
+ longitudes[planet] = lon.degrees
95
+ sorted_planets = sorted(longitudes.items(), key=lambda x: x[1])
96
+ PLadder = [p for p, _ in sorted_planets]
97
+ sorted_lons = [lon for _, lon in sorted_planets]
98
+ zone_sizes = [sorted_lons[0]] + [sorted_lons[i+1] - sorted_lons[i] for i in range(6)] + [360 - sorted_lons[6]]
99
+ bordering = [[PLadder[0]]] + [[PLadder[i-1], PLadder[i]] for i in range(1, 7)] + [[PLadder[6]]]
100
+ ZSizes = []
101
+ for i, size in enumerate(zone_sizes):
102
+ bord = bordering[i]
103
+ X = 7 if any(p in ['Sun', 'Moon'] for p in bord) else 6 if any(p in ['Mercury', 'Venus', 'Mars'] for p in bord) else 5
104
+ classification = ('Swallowed' if size <= 1 else 'Tiny' if size <= X else 'Small' if size <= 40 else
105
+ 'Ideal' if 50 <= size <= 52 else 'Normal' if size < 60 else 'Big')
106
+ d, m = int(size), int((size - int(size)) * 60)
107
+ ZSizes.append((f"{d}°{m}'", classification))
108
+ return {'PLadder': PLadder, 'ZSizes': ZSizes, 'longitudes': longitudes}
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
  def plot_pladder(PLadder):
111
+ """Plot the Planetary Ladder with symbols outside the triangle."""
112
  fig, ax = plt.subplots()
113
+ # Plot triangle
114
+ ax.plot([0, 0, 3, 0], [0, 3, 0, 0], 'k-')
115
  # Horizontal lines within triangle
116
+ ax.plot([0, 2], [1, 1], 'k--')
117
+ ax.plot([0, 1], [2, 2], 'k--')
118
+ # Symbol positions outside the triangle
119
  positions = [
120
+ (-0.2, 0), (-0.2, 1), (-0.2, 2), (-0.2, 3), # Symbols 1 to 4
121
+ (1.2, 2.2), (2.2, 1.2), (3.2, 0.2) # Symbols 5 to 7
 
 
 
 
 
122
  ]
123
  for i, (x, y) in enumerate(positions):
124
  ax.text(x, y, planet_symbols[PLadder[i]], ha='center', va='center', fontsize=24)
 
132
  """Process the user query and return text and plot."""
133
  if not query.startswith("PLadder "):
134
  return "Query must start with 'PLadder' followed by date, time, and location.", None
 
135
  query_content = query.split(" ", 1)[1]
136
+ dt, location, error = parse_query(query_content)
137
+ if error:
138
+ return error, None
139
+ utc_dt, lat, lon, error = get_utc_time(dt, location)
140
  if error:
141
  return error, None
 
142
  result = PLadder_ZSizes(utc_dt, lat, lon)
143
  if "error" in result:
144
  return result["error"], None
 
145
  PLadder = result["PLadder"]
146
  ZSizes = result["ZSizes"]
147
  longitudes = result["longitudes"]
 
148
  planet_list = "\n".join([f"{planet_ru[p]}: {lon_to_sign(longitudes[p])}" for p in PLadder])
149
  zones_text = "\n".join([f"Zone {i+1}: {size} ({cls})" for i, (size, cls) in enumerate(ZSizes)])
150
  coords_text = format_coords(lat, lon)
 
151
  fig = plot_pladder(PLadder)
152
  buf = BytesIO()
153
  fig.savefig(buf, format='png', bbox_inches='tight')
154
  buf.seek(0)
155
  img = Image.open(buf)
156
  plt.close(fig)
 
157
  text = f"Planetary Ladder:\n{planet_list}\n\nZone Sizes:\n{zones_text}\n\nCoordinates: {coords_text}"
158
  return text, img
159
 
160
+ # Gradio UI
161
  with gr.Blocks() as interface:
162
  with gr.Row():
163
  with gr.Column(scale=2):
 
167
  with gr.Row():
168
  query_text = gr.Textbox(label="Query", placeholder="Example: PLadder 2023-10-10 12:00 New York")
169
  submit_button = gr.Button("Submit")
170
+ submit_button.click(fn=chat_interface, inputs=query_text, outputs=[output_text, output_image])
 
 
 
 
 
171
 
172
  interface.launch()