CCockrum commited on
Commit
7785b41
·
verified ·
1 Parent(s): aa79e36

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +226 -257
app.py CHANGED
@@ -6,287 +6,283 @@ import pandas as pd
6
  import traceback
7
  import plotly.express as px
8
  import plotly.graph_objects as go
 
 
 
 
 
9
 
10
  class NasaSsdCneosApi:
11
  def __init__(self):
12
  self.fireball_url = "https://ssd-api.jpl.nasa.gov/fireball.api"
13
  self.ca_url = "https://ssd-api.jpl.nasa.gov/cad.api"
14
- self.nea_url = "https://ssd-api.jpl.nasa.gov/sbdb_query.api"
15
- self.scout_url = "https://ssd-api.jpl.nasa.gov/scout.api"
16
-
17
- def get_fireballs(self, limit=10, date_min=None, energy_min=None):
 
 
18
  try:
19
- params = {'limit': limit}
20
- if date_min:
21
- params['date-min'] = date_min
22
- if energy_min:
23
- params['energy-min'] = energy_min
24
-
25
- response = requests.get(self.fireball_url, params=params)
 
 
 
 
 
 
 
 
 
 
26
  response.raise_for_status()
27
- return response.json()
28
- except Exception as e:
29
- print("Fireball API Error:", e)
30
- traceback.print_exc()
 
 
 
 
 
 
 
 
 
 
 
31
  return None
32
-
33
- def get_close_approaches(self, dist_max=None, date_min=None, date_max=None,
34
- h_min=None, h_max=None, v_inf_min=None, v_inf_max=None,
35
- limit=10):
36
- try:
37
- params = {'limit': limit, 'dist-max': dist_max, 'date-min': date_min,
38
- 'date-max': date_max, 'h-min': h_min, 'h-max': h_max,
39
- 'v-inf-min': v_inf_min, 'v-inf-max': v_inf_max, 'sort': 'date'}
40
- params = {k: v for k, v in params.items() if v is not None}
41
-
42
- response = requests.get(self.ca_url, params=params)
43
- response.raise_for_status()
44
- return response.json()
45
- except Exception as e:
46
- print("Close Approaches API Error:", e)
47
- traceback.print_exc()
48
  return None
49
-
50
- def get_nea_data(self, des=None, spk_id=None, h_max=None):
51
- try:
52
- # Build query parameter for NEAs - select asteroid with NEA flag
53
- query_params = {
54
- 'sb-nea': 'true' # Filter for Near-Earth Asteroids
55
- }
56
 
57
- if des:
58
- query_params['sb-spk'] = des
59
- if spk_id:
60
- query_params['sb-spkid'] = spk_id
61
- if h_max:
62
- query_params['sb-h-max'] = h_max
63
-
64
- # Add fields to return
65
- query_params['fields'] = 'spkid,full_name,pdes,neo,H,G,diameter,extent,albedo,rot_per,GM,BV,UB,IR,spec_B,spec_T,H_sigma,diameter_sigma,orbit_id,epoch,epoch_mjd,epoch_cal,a,e,i,om,w,ma,ad,n,tp,tp_cal,per,per_y,q,moid,moid_ld,moid_jup'
66
- query_params['limit'] = 100 # Set a reasonable limit
67
-
68
- response = requests.get(self.nea_url, params=query_params)
69
- response.raise_for_status()
70
- return response.json()
71
  except Exception as e:
72
- print("NEA API Error:", e)
73
  traceback.print_exc()
74
  return None
75
 
76
- def get_scout_data(self, limit=10, nea_comet="NEA"):
77
- try:
78
- params = {'limit': limit}
79
- if nea_comet:
80
- params['nea-comet'] = nea_comet.lower()
 
 
 
 
81
 
82
- response = requests.get(self.scout_url, params=params)
83
- response.raise_for_status()
84
- return response.json()
85
- except Exception as e:
86
- print("Scout API Error:", e)
87
- traceback.print_exc()
88
- return None
 
 
 
 
 
 
 
 
 
 
89
 
90
  def format_response(self, data, format_type):
 
91
  try:
92
  if not data:
 
93
  return None
94
 
95
- fields = data.get('fields')
 
96
  rows = data.get('data')
97
 
98
  if not fields or not rows:
 
 
99
  return None
100
 
 
101
  df = pd.DataFrame([dict(zip(fields, row)) for row in rows])
 
 
 
 
 
 
 
 
102
 
 
103
  if format_type == 'fireballs':
104
- return df.rename(columns={
105
- 'date': 'Date/Time', 'energy': 'Energy (kt)',
106
- 'impact-e': 'Impact Energy (10^10 J)', 'lat': 'Latitude',
107
- 'lon': 'Longitude', 'alt': 'Altitude (km)',
 
 
 
 
108
  'vel': 'Velocity (km/s)'
109
- })
 
 
 
110
 
111
  elif format_type == 'close_approaches':
112
- return df.rename(columns={
113
- 'des': 'Object', 'orbit_id': 'Orbit ID', 'cd': 'Time (TDB)',
114
- 'dist': 'Nominal Distance (au)', 'dist_min': 'Minimum Distance (au)',
115
- 'dist_max': 'Maximum Distance (au)', 'v_rel': 'Velocity (km/s)',
 
 
 
 
116
  'h': 'H (mag)'
117
- })
118
-
119
- elif format_type == 'nea':
120
- name_columns = {
121
- 'full_name': 'Full Name', 'pdes': 'Designation',
122
- 'H': 'Absolute Magnitude (mag)', 'diameter': 'Diameter (km)',
123
- 'q': 'Perihelion (au)', 'ad': 'Aphelion (au)',
124
- 'i': 'Inclination (deg)', 'e': 'Eccentricity',
125
- 'moid': 'MOID (au)', 'moid_ld': 'MOID (LD)'
126
  }
127
- # Use only columns that exist in the dataframe
128
- valid_columns = {k: v for k, v in name_columns.items() if k in df.columns}
129
- return df.rename(columns=valid_columns)
130
-
131
- elif format_type == 'scout':
132
- # Handle Scout API response - column names may vary
133
- # Adjust these column mappings based on actual response structure
134
- if 'score' in df.columns:
135
- df = df.rename(columns={
136
- 'object': 'Object', 'score': 'Rating',
137
- 'diameter': 'Diameter (m)', 'ca_dist': 'Close Approach',
138
- 'nobs': 'Observations'
139
- })
140
- return df
141
 
142
  return df
 
143
  except Exception as e:
144
- print("Data formatting error:", e)
145
  traceback.print_exc()
146
  return None
147
 
148
 
149
- # Gradio Interface Functions
150
 
151
  def fetch_fireballs(limit, date_min, energy_min):
152
- api = NasaSsdCneosApi()
153
-
154
- # Convert empty strings to None
155
- date_min = date_min if date_min else None
156
- energy_min = float(energy_min) if energy_min else None
157
-
158
- data = api.get_fireballs(
159
- limit=int(limit),
160
- date_min=date_min,
161
- energy_min=energy_min
162
- )
163
-
164
- df = api.format_response(data, 'fireballs')
165
- if df is None or df.empty:
166
- return "No data available", None
167
-
168
- # Create world map of fireballs
169
- if 'Latitude' in df.columns and 'Longitude' in df.columns:
170
- fig = px.scatter_geo(df,
171
- lat='Latitude',
172
- lon='Longitude',
173
- size='Energy (kt)',
174
- hover_name='Date/Time',
175
- projection='natural earth',
176
- title='Fireball Events')
177
 
178
- return df, fig
179
-
180
- return df, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
 
182
  def fetch_close_approaches(limit, dist_max, date_min, date_max, h_min, h_max, v_inf_min, v_inf_max):
183
- api = NasaSsdCneosApi()
184
-
185
- # Convert empty strings to None
186
- dist_max = float(dist_max) if dist_max else None
187
- date_min = date_min if date_min else None
188
- date_max = date_max if date_max else None
189
- h_min = float(h_min) if h_min else None
190
- h_max = float(h_max) if h_max else None
191
- v_inf_min = float(v_inf_min) if v_inf_min else None
192
- v_inf_max = float(v_inf_max) if v_inf_max else None
193
-
194
- data = api.get_close_approaches(
195
- limit=int(limit),
196
- dist_max=dist_max,
197
- date_min=date_min,
198
- date_max=date_max,
199
- h_min=h_min,
200
- h_max=h_max,
201
- v_inf_min=v_inf_min,
202
- v_inf_max=v_inf_max
203
- )
204
-
205
- df = api.format_response(data, 'close_approaches')
206
- if df is None or df.empty:
207
- return "No data available", None
208
-
209
- # Create scatter plot of distance vs velocity
210
- fig = px.scatter(df,
211
- x='Nominal Distance (au)',
212
- y='Velocity (km/s)',
213
- hover_name='Object',
214
- size='H (mag)',
215
- color='H (mag)',
216
- title='Close Approaches - Distance vs Velocity')
217
-
218
- return df, fig
219
-
220
- def fetch_nea_data(des, spk_id, h_max):
221
- api = NasaSsdCneosApi()
222
-
223
- # Convert empty strings to None
224
- des = des if des else None
225
- spk_id = spk_id if spk_id else None
226
- h_max = float(h_max) if h_max else None
227
-
228
- data = api.get_nea_data(
229
- des=des,
230
- spk_id=spk_id,
231
- h_max=h_max
232
- )
233
-
234
- df = api.format_response(data, 'nea')
235
- if df is None or df.empty:
236
- return "No data available", None
237
-
238
- # Create a scatter plot of perihelion vs aphelion colored by inclination
239
- if not df.empty and 'Perihelion (au)' in df.columns and 'Aphelion (au)' in df.columns:
240
- fig = px.scatter(df,
241
- x='Perihelion (au)',
242
- y='Aphelion (au)',
243
- hover_name='Designation' if 'Designation' in df.columns else None,
244
- color='Inclination (deg)' if 'Inclination (deg)' in df.columns else None,
245
- size='Diameter (km)' if 'Diameter (km)' in df.columns else None,
246
- title='NEA Orbital Parameters')
247
 
248
- return df, fig
249
-
250
- return df, None
251
-
252
- def fetch_scout_data(limit, nea_comet):
253
- api = NasaSsdCneosApi()
254
-
255
- data = api.get_scout_data(
256
- limit=int(limit),
257
- nea_comet=nea_comet
258
- )
259
-
260
- df = api.format_response(data, 'scout')
261
- if df is None or df.empty:
262
- return "No data available", None
263
-
264
- # Create a scatter plot based on available columns
265
- if not df.empty:
266
- # Use columns that are available in the dataframe
267
- x_col = 'Diameter (m)' if 'Diameter (m)' in df.columns else df.columns[0]
268
- y_col = 'Close Approach' if 'Close Approach' in df.columns else df.columns[1]
269
- hover_col = 'Object' if 'Object' in df.columns else None
270
- color_col = 'Rating' if 'Rating' in df.columns else None
271
- size_col = 'Observations' if 'Observations' in df.columns else None
272
 
273
- fig = px.scatter(df,
274
- x=x_col,
275
- y=y_col,
276
- hover_name=hover_col,
277
- color=color_col,
278
- size=size_col,
279
- title='Scout Objects')
280
 
281
- return df, fig
282
-
283
- return df, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
 
285
  # Create Gradio interface
286
  with gr.Blocks(title="NASA SSD/CNEOS API Explorer") as demo:
287
  gr.Markdown("# NASA SSD/CNEOS API Explorer")
288
  gr.Markdown("Access data from NASA's Center for Near Earth Object Studies")
289
 
 
 
 
290
  with gr.Tab("Fireballs"):
291
  gr.Markdown("### Fireball Events")
292
  gr.Markdown("Get information about recent fireball events detected by sensors.")
@@ -300,7 +296,9 @@ with gr.Blocks(title="NASA SSD/CNEOS API Explorer") as demo:
300
  fireball_results = gr.DataFrame(label="Fireball Results")
301
  fireball_map = gr.Plot(label="Fireball Map")
302
 
303
- fireball_submit.click(fetch_fireballs, inputs=[fireball_limit, fireball_date, fireball_energy], outputs=[fireball_results, fireball_map])
 
 
304
 
305
  with gr.Tab("Close Approaches"):
306
  gr.Markdown("### Close Approaches")
@@ -324,42 +322,13 @@ with gr.Blocks(title="NASA SSD/CNEOS API Explorer") as demo:
324
  inputs=[ca_limit, ca_dist_max, ca_date_min, ca_date_max, ca_h_min, ca_h_max, ca_v_min, ca_v_max],
325
  outputs=[ca_results, ca_plot])
326
 
327
- with gr.Tab("NEA Data"):
328
- gr.Markdown("### Near-Earth Asteroid Data")
329
- gr.Markdown("Get information about specific near-Earth asteroids.")
330
- with gr.Row():
331
- with gr.Column():
332
- nea_des = gr.Textbox(label="Designation", placeholder="e.g. 2020 SW")
333
- nea_spk = gr.Textbox(label="SPK-ID", placeholder="e.g. 54101815")
334
- nea_h_max = gr.Textbox(label="Maximum H (mag)", placeholder="e.g. 25")
335
- nea_submit = gr.Button("Fetch NEA Data")
336
- with gr.Column():
337
- nea_results = gr.DataFrame(label="NEA Results")
338
- nea_plot = gr.Plot(label="NEA Orbital Parameters")
339
-
340
- nea_submit.click(fetch_nea_data, inputs=[nea_des, nea_spk, nea_h_max], outputs=[nea_results, nea_plot])
341
-
342
- with gr.Tab("Scout Data"):
343
- gr.Markdown("### Scout System Data")
344
- gr.Markdown("Get information about newly discovered objects from NASA's Scout system.")
345
- with gr.Row():
346
- with gr.Column():
347
- scout_limit = gr.Slider(minimum=1, maximum=100, value=10, step=1, label="Limit")
348
- scout_type = gr.Radio(["NEA", "comet"], label="Object Type", value="NEA")
349
- scout_submit = gr.Button("Fetch Scout Data")
350
- with gr.Column():
351
- scout_results = gr.DataFrame(label="Scout Results")
352
- scout_plot = gr.Plot(label="Scout Objects Plot")
353
-
354
- scout_submit.click(fetch_scout_data, inputs=[scout_limit, scout_type], outputs=[scout_results, scout_plot])
355
-
356
  gr.Markdown("### About")
357
  gr.Markdown("""
358
  This application provides access to NASA's Solar System Dynamics (SSD) and Center for Near Earth Object Studies (CNEOS) API.
359
 
360
  Data is retrieved in real-time from NASA's servers. All data is courtesy of NASA/JPL-Caltech.
361
 
362
- Created by [Your Name] using Gradio and Hugging Face Spaces.
363
  """)
364
 
365
  # Create requirements.txt file
 
6
  import traceback
7
  import plotly.express as px
8
  import plotly.graph_objects as go
9
+ import logging
10
+
11
+ # Set up logging
12
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
13
+ logger = logging.getLogger(__name__)
14
 
15
  class NasaSsdCneosApi:
16
  def __init__(self):
17
  self.fireball_url = "https://ssd-api.jpl.nasa.gov/fireball.api"
18
  self.ca_url = "https://ssd-api.jpl.nasa.gov/cad.api"
19
+
20
+ # For debugging - print response details if True
21
+ self.debug_mode = True
22
+
23
+ def _make_api_request(self, url, params, name="API"):
24
+ """Generic API request handler with error handling and debugging"""
25
  try:
26
+ # Clean up None values and empty strings
27
+ clean_params = {k: v for k, v in params.items() if v is not None and v != ""}
28
+
29
+ # Log the request in debug mode
30
+ if self.debug_mode:
31
+ logger.info(f"{name} Request - URL: {url}")
32
+ logger.info(f"{name} Request - Params: {clean_params}")
33
+
34
+ # Make the request
35
+ response = requests.get(url, params=clean_params)
36
+
37
+ # Log the response status and content in debug mode
38
+ if self.debug_mode:
39
+ logger.info(f"{name} Response - Status: {response.status_code}")
40
+ logger.info(f"{name} Response - Content Preview: {response.text[:500]}...")
41
+
42
+ # Check for HTTP errors
43
  response.raise_for_status()
44
+
45
+ # Parse JSON response
46
+ data = response.json()
47
+
48
+ # Check for API-specific error messages
49
+ if isinstance(data, dict) and "error" in data:
50
+ logger.error(f"{name} API Error: {data['error']}")
51
+ return None
52
+
53
+ return data
54
+
55
+ except requests.exceptions.HTTPError as http_err:
56
+ logger.error(f"{name} HTTP Error: {http_err}")
57
+ if self.debug_mode and hasattr(http_err, 'response'):
58
+ logger.error(f"Response content: {http_err.response.text}")
59
  return None
60
+
61
+ except json.JSONDecodeError as json_err:
62
+ logger.error(f"{name} JSON Decode Error: {json_err}")
63
+ if self.debug_mode and 'response' in locals():
64
+ logger.error(f"Raw response: {response.text}")
 
 
 
 
 
 
 
 
 
 
 
65
  return None
 
 
 
 
 
 
 
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  except Exception as e:
68
+ logger.error(f"{name} General Error: {e}")
69
  traceback.print_exc()
70
  return None
71
 
72
+ def get_fireballs(self, limit=10, date_min=None, energy_min=None):
73
+ """Get fireball events from NASA CNEOS API"""
74
+ params = {'limit': limit}
75
+ if date_min:
76
+ params['date-min'] = date_min
77
+ if energy_min:
78
+ params['energy-min'] = energy_min
79
+
80
+ return self._make_api_request(self.fireball_url, params, "Fireball API")
81
 
82
+ def get_close_approaches(self, dist_max=None, date_min=None, date_max=None,
83
+ h_min=None, h_max=None, v_inf_min=None, v_inf_max=None,
84
+ limit=10):
85
+ """Get close approach data from NASA CNEOS API"""
86
+ params = {
87
+ 'limit': limit,
88
+ 'dist-max': dist_max,
89
+ 'date-min': date_min,
90
+ 'date-max': date_max,
91
+ 'h-min': h_min,
92
+ 'h-max': h_max,
93
+ 'v-inf-min': v_inf_min,
94
+ 'v-inf-max': v_inf_max,
95
+ 'sort': 'date'
96
+ }
97
+
98
+ return self._make_api_request(self.ca_url, params, "Close Approaches API")
99
 
100
  def format_response(self, data, format_type):
101
+ """Format JSON response from API into a pandas DataFrame"""
102
  try:
103
  if not data:
104
+ logger.warning(f"No data received for {format_type} format")
105
  return None
106
 
107
+ # Some API responses use 'signature' field instead of 'fields'
108
+ fields = data.get('fields', data.get('signature'))
109
  rows = data.get('data')
110
 
111
  if not fields or not rows:
112
+ logger.warning(f"Missing fields or data rows for {format_type} format")
113
+ logger.debug(f"Data structure: {data.keys()}")
114
  return None
115
 
116
+ # Create DataFrame from the API response
117
  df = pd.DataFrame([dict(zip(fields, row)) for row in rows])
118
+
119
+ if df.empty:
120
+ logger.warning(f"Empty DataFrame created for {format_type}")
121
+ return None
122
+
123
+ # Log available columns for debugging
124
+ if self.debug_mode:
125
+ logger.info(f"Available columns in {format_type} response: {df.columns.tolist()}")
126
 
127
+ # Format based on data type
128
  if format_type == 'fireballs':
129
+ # Only rename columns that exist in the DataFrame
130
+ rename_map = {
131
+ 'date': 'Date/Time',
132
+ 'energy': 'Energy (kt)',
133
+ 'impact-e': 'Impact Energy (10^10 J)',
134
+ 'lat': 'Latitude',
135
+ 'lon': 'Longitude',
136
+ 'alt': 'Altitude (km)',
137
  'vel': 'Velocity (km/s)'
138
+ }
139
+ # Filter rename map to only include columns that exist
140
+ valid_rename = {k: v for k, v in rename_map.items() if k in df.columns}
141
+ return df.rename(columns=valid_rename)
142
 
143
  elif format_type == 'close_approaches':
144
+ rename_map = {
145
+ 'des': 'Object',
146
+ 'orbit_id': 'Orbit ID',
147
+ 'cd': 'Time (TDB)',
148
+ 'dist': 'Nominal Distance (au)',
149
+ 'dist_min': 'Minimum Distance (au)',
150
+ 'dist_max': 'Maximum Distance (au)',
151
+ 'v_rel': 'Velocity (km/s)',
152
  'h': 'H (mag)'
 
 
 
 
 
 
 
 
 
153
  }
154
+ valid_rename = {k: v for k, v in rename_map.items() if k in df.columns}
155
+ return df.rename(columns=valid_rename)
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  return df
158
+
159
  except Exception as e:
160
+ logger.error(f"Data formatting error for {format_type}: {e}")
161
  traceback.print_exc()
162
  return None
163
 
164
 
165
+ # Gradio Interface Functions with better error handling
166
 
167
  def fetch_fireballs(limit, date_min, energy_min):
168
+ """Fetch fireball data for Gradio interface"""
169
+ try:
170
+ api = NasaSsdCneosApi()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
+ # Process inputs
173
+ date_min = date_min.strip() if date_min else None
174
+ try:
175
+ energy_min = float(energy_min) if energy_min else None
176
+ except ValueError:
177
+ return f"Error: Invalid energy value '{energy_min}'. Please enter a valid number.", None
178
+
179
+ data = api.get_fireballs(
180
+ limit=int(limit),
181
+ date_min=date_min,
182
+ energy_min=energy_min
183
+ )
184
+
185
+ if not data:
186
+ return "No data returned from API. There might be an issue with the connection or parameters.", None
187
+
188
+ df = api.format_response(data, 'fireballs')
189
+ if df is None or df.empty:
190
+ return "No fireball data available for the specified parameters.", None
191
+
192
+ # Create world map of fireballs
193
+ if 'Latitude' in df.columns and 'Longitude' in df.columns:
194
+ try:
195
+ # Create size column if Energy (kt) is not available
196
+ size_col = 'Energy (kt)' if 'Energy (kt)' in df.columns else None
197
+
198
+ fig = px.scatter_geo(df,
199
+ lat='Latitude',
200
+ lon='Longitude',
201
+ size=size_col,
202
+ hover_name='Date/Time' if 'Date/Time' in df.columns else None,
203
+ projection='natural earth',
204
+ title='Fireball Events')
205
+
206
+ return df, fig
207
+ except Exception as plot_err:
208
+ logger.error(f"Error creating fireball plot: {plot_err}")
209
+ return df, None
210
+
211
+ return df, None
212
+ except Exception as e:
213
+ logger.error(f"Error in fetch_fireballs: {e}")
214
+ traceback.print_exc()
215
+ return f"An error occurred: {str(e)}", None
216
 
217
  def fetch_close_approaches(limit, dist_max, date_min, date_max, h_min, h_max, v_inf_min, v_inf_max):
218
+ """Fetch close approach data for Gradio interface"""
219
+ try:
220
+ api = NasaSsdCneosApi()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
+ # Process inputs with error handling
223
+ try:
224
+ dist_max = float(dist_max) if dist_max else None
225
+ h_min = float(h_min) if h_min else None
226
+ h_max = float(h_max) if h_max else None
227
+ v_inf_min = float(v_inf_min) if v_inf_min else None
228
+ v_inf_max = float(v_inf_max) if v_inf_max else None
229
+ except ValueError as ve:
230
+ return f"Error: Invalid numeric input - {str(ve)}", None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
 
232
+ date_min = date_min.strip() if date_min else None
233
+ date_max = date_max.strip() if date_max else None
 
 
 
 
 
234
 
235
+ data = api.get_close_approaches(
236
+ limit=int(limit),
237
+ dist_max=dist_max,
238
+ date_min=date_min,
239
+ date_max=date_max,
240
+ h_min=h_min,
241
+ h_max=h_max,
242
+ v_inf_min=v_inf_min,
243
+ v_inf_max=v_inf_max
244
+ )
245
+
246
+ if not data:
247
+ return "No data returned from API. There might be an issue with the connection or parameters.", None
248
+
249
+ df = api.format_response(data, 'close_approaches')
250
+ if df is None or df.empty:
251
+ return "No close approach data available for the specified parameters.", None
252
+
253
+ # Create scatter plot
254
+ try:
255
+ x_col = 'Nominal Distance (au)' if 'Nominal Distance (au)' in df.columns else df.columns[0]
256
+ y_col = 'Velocity (km/s)' if 'Velocity (km/s)' in df.columns else df.columns[1]
257
+ hover_col = 'Object' if 'Object' in df.columns else None
258
+ size_col = 'H (mag)' if 'H (mag)' in df.columns else None
259
+ color_col = 'H (mag)' if 'H (mag)' in df.columns else None
260
+
261
+ fig = px.scatter(df,
262
+ x=x_col,
263
+ y=y_col,
264
+ hover_name=hover_col,
265
+ size=size_col,
266
+ color=color_col,
267
+ title='Close Approaches - Distance vs Velocity')
268
+
269
+ return df, fig
270
+ except Exception as plot_err:
271
+ logger.error(f"Error creating close approach plot: {plot_err}")
272
+ return df, None
273
+ except Exception as e:
274
+ logger.error(f"Error in fetch_close_approaches: {e}")
275
+ traceback.print_exc()
276
+ return f"An error occurred: {str(e)}", None
277
 
278
  # Create Gradio interface
279
  with gr.Blocks(title="NASA SSD/CNEOS API Explorer") as demo:
280
  gr.Markdown("# NASA SSD/CNEOS API Explorer")
281
  gr.Markdown("Access data from NASA's Center for Near Earth Object Studies")
282
 
283
+ # Error display area
284
+ error_box = gr.Textbox(label="Status", visible=True)
285
+
286
  with gr.Tab("Fireballs"):
287
  gr.Markdown("### Fireball Events")
288
  gr.Markdown("Get information about recent fireball events detected by sensors.")
 
296
  fireball_results = gr.DataFrame(label="Fireball Results")
297
  fireball_map = gr.Plot(label="Fireball Map")
298
 
299
+ fireball_submit.click(fetch_fireballs,
300
+ inputs=[fireball_limit, fireball_date, fireball_energy],
301
+ outputs=[fireball_results, fireball_map])
302
 
303
  with gr.Tab("Close Approaches"):
304
  gr.Markdown("### Close Approaches")
 
322
  inputs=[ca_limit, ca_dist_max, ca_date_min, ca_date_max, ca_h_min, ca_h_max, ca_v_min, ca_v_max],
323
  outputs=[ca_results, ca_plot])
324
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
  gr.Markdown("### About")
326
  gr.Markdown("""
327
  This application provides access to NASA's Solar System Dynamics (SSD) and Center for Near Earth Object Studies (CNEOS) API.
328
 
329
  Data is retrieved in real-time from NASA's servers. All data is courtesy of NASA/JPL-Caltech.
330
 
331
+ Created using Gradio and Hugging Face Spaces.
332
  """)
333
 
334
  # Create requirements.txt file