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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +98 -71
app.py CHANGED
@@ -2,7 +2,6 @@ import gradio as gr
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,12 +20,67 @@ planet_symbols = {
21
  'Mars': '♂', 'Jupiter': '♃', 'Saturn': '♄'
22
  }
23
 
24
- # Initialize geolocator and timezone finder
25
- geolocator = Nominatim(user_agent="pladder_app")
26
- tf = TimezoneFinder()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- # Function to convert longitude to zodiac sign and degrees
29
  def lon_to_sign(lon):
 
30
  signs = ["Овен", "Телец", "Близнецы", "Рак", "Лев", "Дева",
31
  "Весы", "Скорпион", "Стрелец", "Козерог", "Водолей", "Рыбы"]
32
  sign_index = int(lon // 30)
@@ -34,16 +88,15 @@ def lon_to_sign(lon):
34
  minutes = int((lon % 1) * 60)
35
  return f"{signs[sign_index]} {degrees}°{minutes}'"
36
 
37
- # Function to calculate PLadder and zone sizes (unchanged)
38
- def PLadder_ZSizes(date_time_iso, lat, lon):
39
  try:
40
- dt = datetime.fromisoformat(date_time_iso)
41
- if not 1900 <= dt.year <= 2050:
42
- return {"error": "Дата вне диапазона (1900–2050)."}
43
 
44
  planets = load('de421.bsp')
45
  earth = planets['earth']
46
- observer = earth + Topos(latitude_degrees=float(lat), longitude_degrees=float(lon))
47
 
48
  planet_objects = {
49
  'Sun': planets['sun'], 'Moon': planets['moon'], 'Mercury': planets['mercury'],
@@ -52,7 +105,7 @@ def PLadder_ZSizes(date_time_iso, lat, lon):
52
  }
53
 
54
  ts = load.timescale()
55
- t = ts.utc(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
56
 
57
  longitudes = {}
58
  for planet, obj in planet_objects.items():
@@ -78,18 +131,27 @@ def PLadder_ZSizes(date_time_iso, lat, lon):
78
 
79
  return {'PLadder': PLadder, 'ZSizes': ZSizes, 'longitudes': longitudes}
80
 
81
- except ValueError:
82
- return {"error": "Неверный формат даты и времени."}
83
  except Exception as e:
84
- return {"error": f"Ошибка: {str(e)}"}
85
 
86
- # Function to plot the planetary ladder (unchanged)
87
  def plot_pladder(PLadder):
 
88
  fig, ax = plt.subplots()
89
- ax.plot([0, 1.5, 3, 0], [0, 3, 0, 0], 'k-') # Triangle
90
- ax.plot([0, 3], [1, 1], 'k--') # Horizontal lines
91
- ax.plot([0, 3], [2, 2], 'k--')
92
- positions = [(0.2, 0.2), (0.2, 1.2), (0.2, 2.2), (1.5, 3.2), (2.8, 2.2), (2.8, 1.2), (2.8, 0.2)]
 
 
 
 
 
 
 
 
 
 
 
93
  for i, (x, y) in enumerate(positions):
94
  ax.text(x, y, planet_symbols[PLadder[i]], ha='center', va='center', fontsize=24)
95
  ax.set_xlim(-0.5, 3.5)
@@ -98,63 +160,28 @@ def plot_pladder(PLadder):
98
  ax.axis('off')
99
  return fig
100
 
101
- # Main interface function (reworked)
102
  def chat_interface(query):
103
- # Check for valid command
104
  if not query.startswith("PLadder "):
105
- return "Запрос должен начинаться с 'PLadder' и содержать дату, время и местоположение.", None
106
 
107
- # Extract date, time, and location
108
- query_rest = query[len("PLadder "):]
109
- try:
110
- parsed_dt, skipped = parser.parse(query_rest, fuzzy_with_tokens=True)
111
- location = " ".join(skipped)
112
- if not location:
113
- return "Укажите местоположение (город или населённый пункт).", None
114
- except ValueError:
115
- return "Неверный формат даты и времени.", None
116
 
117
- # Geocode the location to get latitude and longitude
118
- try:
119
- location_data = geolocator.geocode(location)
120
- if location_data:
121
- lat = location_data.latitude
122
- lon = location_data.longitude
123
- else:
124
- return f"Не удалось найти координаты для '{location}'.", None
125
- except Exception as e:
126
- return f"Ошибка геокодирования: {str(e)}", None
127
-
128
- # Determine time zone from coordinates
129
- tz_name = tf.timezone_at(lng=lon, lat=lat)
130
- if tz_name is None:
131
- return f"Не удалось определить часовой пояс для '{location}'.", None
132
- tz = pytz.timezone(tz_name)
133
-
134
- # Localize the parsed local time and convert to UTC
135
- try:
136
- timezone_aware_dt = tz.localize(parsed_dt)
137
- except pytz.exceptions.AmbiguousTimeError:
138
- # Handle ambiguous times (e.g., DST transitions) by defaulting to standard time
139
- timezone_aware_dt = tz.localize(parsed_dt, is_dst=False)
140
- utc_dt = timezone_aware_dt.astimezone(pytz.utc)
141
- date_time_iso = utc_dt.isoformat()
142
-
143
- # Pass UTC time and coordinates to PLadder_ZSizes
144
- result = PLadder_ZSizes(date_time_iso, str(lat), str(lon))
145
  if "error" in result:
146
  return result["error"], None
147
 
148
- # Extract results
149
  PLadder = result["PLadder"]
150
  ZSizes = result["ZSizes"]
151
  longitudes = result["longitudes"]
152
 
153
- # Format output text
154
  planet_list = "\n".join([f"{planet_ru[p]}: {lon_to_sign(longitudes[p])}" for p in PLadder])
155
- zones_text = "\n".join([f"Зона {i+1}: {size} ({cls})" for i, (size, cls) in enumerate(ZSizes)])
 
156
 
157
- # Generate plot
158
  fig = plot_pladder(PLadder)
159
  buf = BytesIO()
160
  fig.savefig(buf, format='png', bbox_inches='tight')
@@ -162,19 +189,19 @@ def chat_interface(query):
162
  img = Image.open(buf)
163
  plt.close(fig)
164
 
165
- # Combine text output
166
- text = f"Планетарная лестница:\n{planet_list}\n\nРазмеры зон:\n{zones_text}"
167
  return text, img
168
 
169
- # Gradio UI (reworked for single query field)
170
  with gr.Blocks() as interface:
171
- query_text = gr.Textbox(label="Запрос", placeholder="Пример: PLadder 2023-10-10 12:00 New York")
172
- submit_button = gr.Button("Отправить")
173
  with gr.Row():
174
  with gr.Column(scale=2):
175
- output_text = gr.Textbox(label="Ответ", lines=10)
176
  with gr.Column(scale=1):
177
- output_image = gr.Image(label="График планетарной лестницы")
 
 
 
178
 
179
  submit_button.click(
180
  fn=chat_interface,
 
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
  '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)
 
88
  minutes = int((lon % 1) * 60)
89
  return f"{signs[sign_index]} {degrees}°{minutes}'"
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'],
 
105
  }
106
 
107
  ts = load.timescale()
108
+ t = ts.from_datetime(utc_dt)
109
 
110
  longitudes = {}
111
  for planet, obj in planet_objects.items():
 
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)
157
  ax.set_xlim(-0.5, 3.5)
 
160
  ax.axis('off')
161
  return fig
162
 
 
163
  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')
 
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):
199
+ output_text = gr.Textbox(label="Response", lines=10)
200
  with gr.Column(scale=1):
201
+ output_image = gr.Image(label="Planetary Ladder Plot")
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,