CCockrum commited on
Commit
985d262
·
verified ·
1 Parent(s): 3b9597a

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +386 -0
app.py ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ from datetime import datetime, timedelta
4
+ import gradio as gr
5
+ import pandas as pd
6
+ import plotly.express as px
7
+ import plotly.graph_objects as go
8
+
9
+ class NasaSsdCneosApi:
10
+ """
11
+ A class to interact with NASA's SSD/CNEOS API.
12
+ Provides methods to access data on near-Earth objects, close approaches, and more.
13
+ """
14
+
15
+ def __init__(self):
16
+ self.base_url = "https://ssd-api.jpl.nasa.gov"
17
+ self.cneos_url = f"{self.base_url}/cneos"
18
+ self.fireball_url = f"{self.cneos_url}/fireballs"
19
+ self.ca_url = f"{self.cneos_url}/close_approaches"
20
+ self.nea_url = f"{self.cneos_url}/nea"
21
+ self.scout_url = f"{self.cneos_url}/scout"
22
+
23
+ def get_fireballs(self, limit=10, date_min=None, energy_min=None):
24
+ """Get information about recent fireballs"""
25
+ params = {'limit': limit}
26
+ if date_min:
27
+ params['date-min'] = date_min
28
+ if energy_min:
29
+ params['energy-min'] = energy_min
30
+
31
+ response = requests.get(self.fireball_url, params=params)
32
+ if response.status_code == 200:
33
+ return response.json()
34
+ else:
35
+ print(f"Error: {response.status_code}")
36
+ return None
37
+
38
+ def get_close_approaches(self, dist_max=None, date_min=None, date_max=None,
39
+ h_min=None, h_max=None, v_inf_min=None, v_inf_max=None,
40
+ limit=10):
41
+ """Get information about close approaches of near-Earth objects"""
42
+
43
+ params = {'limit': limit}
44
+ if dist_max:
45
+ params['dist-max'] = dist_max
46
+ if date_min:
47
+ params['date-min'] = date_min
48
+ if date_max:
49
+ params['date-max'] = date_max
50
+ if h_min:
51
+ params['h-min'] = h_min
52
+ if h_max:
53
+ params['h-max'] = h_max
54
+ if v_inf_min:
55
+ params['v-inf-min'] = v_inf_min
56
+ if v_inf_max:
57
+ params['v-inf-max'] = v_inf_max
58
+
59
+ response = requests.get(self.ca_url, params=params)
60
+ if response.status_code == 200:
61
+ return response.json()
62
+ else:
63
+ print(f"Error: {response.status_code}")
64
+ return None
65
+
66
+ def get_nea_data(self, spk_id=None, des=None, h_max=None):
67
+ """Get data about specific near-Earth asteroids"""
68
+ params = {}
69
+ if spk_id:
70
+ params['spk-id'] = spk_id
71
+ if des:
72
+ params['des'] = des
73
+ if h_max:
74
+ params['h-max'] = h_max
75
+
76
+ response = requests.get(self.nea_url, params=params)
77
+ if response.status_code == 200:
78
+ return response.json()
79
+ else:
80
+ print(f"Error: {response.status_code}")
81
+ return None
82
+
83
+ def get_scout_data(self, nea_comet='NEA', limit=10):
84
+ """Get Scout system data for newly discovered objects"""
85
+ params = {'nea-comet': nea_comet, 'limit': limit}
86
+
87
+ response = requests.get(self.scout_url, params=params)
88
+ if response.status_code == 200:
89
+ return response.json()
90
+ else:
91
+ print(f"Error: {response.status_code}")
92
+ return None
93
+
94
+ def format_response(self, data, format_type):
95
+ """Format the response data for better readability"""
96
+ if not data:
97
+ return None
98
+
99
+ if format_type == 'fireballs':
100
+ result = []
101
+ for fireball in data.get('data', []):
102
+ entry = {
103
+ 'Date/Time': fireball.get('date'),
104
+ 'Energy (kt)': fireball.get('energy'),
105
+ 'Impact Energy (10^10 J)': fireball.get('impact-e'),
106
+ 'Latitude': fireball.get('lat'),
107
+ 'Longitude': fireball.get('lon'),
108
+ 'Altitude (km)': fireball.get('alt'),
109
+ 'Velocity (km/s)': fireball.get('vel')
110
+ }
111
+ result.append(entry)
112
+ return pd.DataFrame(result)
113
+
114
+ elif format_type == 'close_approaches':
115
+ result = []
116
+ for ca in data.get('data', []):
117
+ entry = {
118
+ 'Object': ca.get('des'),
119
+ 'Orbit ID': ca.get('orbit_id'),
120
+ 'Time (TDB)': ca.get('cd'),
121
+ 'Nominal Distance (au)': ca.get('dist'),
122
+ 'Minimum Distance (au)': ca.get('dist_min'),
123
+ 'Maximum Distance (au)': ca.get('dist_max'),
124
+ 'Velocity (km/s)': ca.get('v_rel'),
125
+ 'H (mag)': ca.get('h')
126
+ }
127
+ result.append(entry)
128
+ return pd.DataFrame(result)
129
+
130
+ elif format_type == 'nea':
131
+ result = []
132
+ for nea in data.get('data', []):
133
+ entry = {
134
+ 'Designation': nea.get('des'),
135
+ 'H (mag)': nea.get('h'),
136
+ 'Diameter (km)': nea.get('diameter'),
137
+ 'Orbit Class': nea.get('orbit_class'),
138
+ 'Perihelion (au)': nea.get('q'),
139
+ 'Aphelion (au)': nea.get('ad'),
140
+ 'Inclination (deg)': nea.get('i'),
141
+ }
142
+ result.append(entry)
143
+ return pd.DataFrame(result)
144
+
145
+ elif format_type == 'scout':
146
+ result = []
147
+ for obj in data.get('data', []):
148
+ entry = {
149
+ 'Object': obj.get('object'),
150
+ 'Rating': obj.get('rating'),
151
+ 'Last Observation': obj.get('last_obs'),
152
+ 'Arc (days)': obj.get('arc'),
153
+ 'Observations': obj.get('n_obs'),
154
+ 'H (mag)': obj.get('h'),
155
+ 'Diameter (m)': obj.get('diameter'),
156
+ 'Close Approach': obj.get('ca_dist_min'),
157
+ 'Velocity (km/s)': obj.get('v_inf')
158
+ }
159
+ result.append(entry)
160
+ return pd.DataFrame(result)
161
+
162
+ return None
163
+
164
+
165
+ # Gradio Interface Functions
166
+
167
+ def fetch_fireballs(limit, date_min, energy_min):
168
+ api = NasaSsdCneosApi()
169
+
170
+ # Convert empty strings to None
171
+ date_min = date_min if date_min else None
172
+ energy_min = float(energy_min) if energy_min else None
173
+
174
+ data = api.get_fireballs(
175
+ limit=int(limit),
176
+ date_min=date_min,
177
+ energy_min=energy_min
178
+ )
179
+
180
+ df = api.format_response(data, 'fireballs')
181
+ if df is None or df.empty:
182
+ return "No data available", None
183
+
184
+ # Create world map of fireballs
185
+ if 'Latitude' in df.columns and 'Longitude' in df.columns:
186
+ fig = px.scatter_geo(df,
187
+ lat='Latitude',
188
+ lon='Longitude',
189
+ size='Energy (kt)',
190
+ hover_name='Date/Time',
191
+ projection='natural earth',
192
+ title='Fireball Events')
193
+
194
+ return df, fig
195
+
196
+ return df, None
197
+
198
+ def fetch_close_approaches(limit, dist_max, date_min, date_max, h_min, h_max, v_inf_min, v_inf_max):
199
+ api = NasaSsdCneosApi()
200
+
201
+ # Convert empty strings to None
202
+ dist_max = float(dist_max) if dist_max else None
203
+ date_min = date_min if date_min else None
204
+ date_max = date_max if date_max else None
205
+ h_min = float(h_min) if h_min else None
206
+ h_max = float(h_max) if h_max else None
207
+ v_inf_min = float(v_inf_min) if v_inf_min else None
208
+ v_inf_max = float(v_inf_max) if v_inf_max else None
209
+
210
+ data = api.get_close_approaches(
211
+ limit=int(limit),
212
+ dist_max=dist_max,
213
+ date_min=date_min,
214
+ date_max=date_max,
215
+ h_min=h_min,
216
+ h_max=h_max,
217
+ v_inf_min=v_inf_min,
218
+ v_inf_max=v_inf_max
219
+ )
220
+
221
+ df = api.format_response(data, 'close_approaches')
222
+ if df is None or df.empty:
223
+ return "No data available", None
224
+
225
+ # Create scatter plot of distance vs velocity
226
+ fig = px.scatter(df,
227
+ x='Nominal Distance (au)',
228
+ y='Velocity (km/s)',
229
+ hover_name='Object',
230
+ size='H (mag)',
231
+ color='H (mag)',
232
+ title='Close Approaches - Distance vs Velocity')
233
+
234
+ return df, fig
235
+
236
+ def fetch_nea_data(des, spk_id, h_max):
237
+ api = NasaSsdCneosApi()
238
+
239
+ # Convert empty strings to None
240
+ des = des if des else None
241
+ spk_id = spk_id if spk_id else None
242
+ h_max = float(h_max) if h_max else None
243
+
244
+ data = api.get_nea_data(
245
+ des=des,
246
+ spk_id=spk_id,
247
+ h_max=h_max
248
+ )
249
+
250
+ df = api.format_response(data, 'nea')
251
+ if df is None or df.empty:
252
+ return "No data available", None
253
+
254
+ # Create a scatter plot of perihelion vs aphelion colored by inclination
255
+ if not df.empty and 'Perihelion (au)' in df.columns:
256
+ fig = px.scatter(df,
257
+ x='Perihelion (au)',
258
+ y='Aphelion (au)',
259
+ hover_name='Designation',
260
+ color='Inclination (deg)',
261
+ size='Diameter (km)',
262
+ title='NEA Orbital Parameters')
263
+
264
+ return df, fig
265
+
266
+ return df, None
267
+
268
+ def fetch_scout_data(limit, nea_comet):
269
+ api = NasaSsdCneosApi()
270
+
271
+ data = api.get_scout_data(
272
+ limit=int(limit),
273
+ nea_comet=nea_comet
274
+ )
275
+
276
+ df = api.format_response(data, 'scout')
277
+ if df is None or df.empty:
278
+ return "No data available", None
279
+
280
+ # Create a scatter plot of diameter vs close approach distance
281
+ if not df.empty and 'Diameter (m)' in df.columns:
282
+ fig = px.scatter(df,
283
+ x='Diameter (m)',
284
+ y='Close Approach',
285
+ hover_name='Object',
286
+ color='Rating',
287
+ size='Observations',
288
+ title='Scout Objects - Size vs Close Approach Distance')
289
+
290
+ return df, fig
291
+
292
+ return df, None
293
+
294
+ # Create Gradio interface
295
+ with gr.Blocks(title="NASA SSD/CNEOS API Explorer") as demo:
296
+ gr.Markdown("# NASA SSD/CNEOS API Explorer")
297
+ gr.Markdown("Access data from NASA's Center for Near Earth Object Studies")
298
+
299
+ with gr.Tab("Fireballs"):
300
+ gr.Markdown("### Fireball Events")
301
+ gr.Markdown("Get information about recent fireball events detected by sensors.")
302
+ with gr.Row():
303
+ with gr.Column():
304
+ fireball_limit = gr.Slider(minimum=1, maximum=100, value=10, step=1, label="Limit")
305
+ fireball_date = gr.Textbox(label="Minimum Date (YYYY-MM-DD)", placeholder="e.g. 2023-01-01")
306
+ fireball_energy = gr.Textbox(label="Minimum Energy (kt)", placeholder="e.g. 0.5")
307
+ fireball_submit = gr.Button("Fetch Fireballs")
308
+ with gr.Column():
309
+ fireball_results = gr.DataFrame(label="Fireball Results")
310
+ fireball_map = gr.Plot(label="Fireball Map")
311
+
312
+ fireball_submit.click(fetch_fireballs, inputs=[fireball_limit, fireball_date, fireball_energy], outputs=[fireball_results, fireball_map])
313
+
314
+ with gr.Tab("Close Approaches"):
315
+ gr.Markdown("### Close Approaches")
316
+ gr.Markdown("Get information about close approaches of near-Earth objects.")
317
+ with gr.Row():
318
+ with gr.Column():
319
+ ca_limit = gr.Slider(minimum=1, maximum=100, value=10, step=1, label="Limit")
320
+ ca_dist_max = gr.Textbox(label="Maximum Distance (AU)", placeholder="e.g. 0.05")
321
+ ca_date_min = gr.Textbox(label="Minimum Date (YYYY-MM-DD)", placeholder="e.g. 2023-01-01")
322
+ ca_date_max = gr.Textbox(label="Maximum Date (YYYY-MM-DD)", placeholder="e.g. 2023-12-31")
323
+ ca_h_min = gr.Textbox(label="Minimum H (mag)", placeholder="e.g. 20")
324
+ ca_h_max = gr.Textbox(label="Maximum H (mag)", placeholder="e.g. 30")
325
+ ca_v_min = gr.Textbox(label="Minimum Velocity (km/s)", placeholder="e.g. 10")
326
+ ca_v_max = gr.Textbox(label="Maximum Velocity (km/s)", placeholder="e.g. 30")
327
+ ca_submit = gr.Button("Fetch Close Approaches")
328
+ with gr.Column():
329
+ ca_results = gr.DataFrame(label="Close Approach Results")
330
+ ca_plot = gr.Plot(label="Close Approach Plot")
331
+
332
+ ca_submit.click(fetch_close_approaches,
333
+ inputs=[ca_limit, ca_dist_max, ca_date_min, ca_date_max, ca_h_min, ca_h_max, ca_v_min, ca_v_max],
334
+ outputs=[ca_results, ca_plot])
335
+
336
+ with gr.Tab("NEA Data"):
337
+ gr.Markdown("### Near-Earth Asteroid Data")
338
+ gr.Markdown("Get information about specific near-Earth asteroids.")
339
+ with gr.Row():
340
+ with gr.Column():
341
+ nea_des = gr.Textbox(label="Designation", placeholder="e.g. 2020 SW")
342
+ nea_spk = gr.Textbox(label="SPK-ID", placeholder="e.g. 54101815")
343
+ nea_h_max = gr.Textbox(label="Maximum H (mag)", placeholder="e.g. 25")
344
+ nea_submit = gr.Button("Fetch NEA Data")
345
+ with gr.Column():
346
+ nea_results = gr.DataFrame(label="NEA Results")
347
+ nea_plot = gr.Plot(label="NEA Orbital Parameters")
348
+
349
+ nea_submit.click(fetch_nea_data, inputs=[nea_des, nea_spk, nea_h_max], outputs=[nea_results, nea_plot])
350
+
351
+ with gr.Tab("Scout Data"):
352
+ gr.Markdown("### Scout System Data")
353
+ gr.Markdown("Get information about newly discovered objects from NASA's Scout system.")
354
+ with gr.Row():
355
+ with gr.Column():
356
+ scout_limit = gr.Slider(minimum=1, maximum=100, value=10, step=1, label="Limit")
357
+ scout_type = gr.Radio(["NEA", "comet"], label="Object Type", value="NEA")
358
+ scout_submit = gr.Button("Fetch Scout Data")
359
+ with gr.Column():
360
+ scout_results = gr.DataFrame(label="Scout Results")
361
+ scout_plot = gr.Plot(label="Scout Objects Plot")
362
+
363
+ scout_submit.click(fetch_scout_data, inputs=[scout_limit, scout_type], outputs=[scout_results, scout_plot])
364
+
365
+ gr.Markdown("### About")
366
+ gr.Markdown("""
367
+ This application provides access to NASA's Solar System Dynamics (SSD) and Center for Near Earth Object Studies (CNEOS) API.
368
+
369
+ Data is retrieved in real-time from NASA's servers. All data is courtesy of NASA/JPL-Caltech.
370
+
371
+ Created by [Your Name] using Gradio and Hugging Face Spaces.
372
+ """)
373
+
374
+ # Create requirements.txt file
375
+ requirements = """
376
+ gradio>=3.50.0
377
+ pandas>=1.5.0
378
+ plotly>=5.14.0
379
+ requests>=2.28.0
380
+ """
381
+
382
+ with open("requirements.txt", "w") as f:
383
+ f.write(requirements)
384
+
385
+ if __name__ == "__main__":
386
+ demo.launch()