nakas commited on
Commit
6268d66
Β·
verified Β·
1 Parent(s): a51c4e8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -59
app.py CHANGED
@@ -225,12 +225,15 @@ class AccurateAirQualityMapper:
225
  value = float(fields[7]) if fields[7].replace('.','').replace('-','').isdigit() else 0
226
  parameter = fields[5]
227
 
228
- # Only include air quality parameters
229
- if parameter not in ['OZONE', 'PM2.5', 'PM10', 'NO2', 'SO2', 'CO']:
230
- continue
231
 
232
  aqi = self.calculate_aqi(parameter, value)
233
 
 
 
 
 
234
  record = {
235
  'DateObserved': fields[0],
236
  'HourObserved': fields[1],
@@ -243,9 +246,10 @@ class AccurateAirQualityMapper:
243
  'Latitude': lat,
244
  'Longitude': lon,
245
  'AQI': aqi,
246
- 'Category': {'Name': self.get_aqi_category(aqi)},
247
  'ReportingArea': fields[3],
248
- 'StateCode': aqs_id[:2] if len(aqs_id) >= 2 else 'US'
 
249
  }
250
 
251
  data.append(record)
@@ -288,39 +292,62 @@ class AccurateAirQualityMapper:
288
  units = item['ReportingUnits']
289
  category = item['Category']['Name']
290
 
291
- # Create popup
292
- popup_content = f"""
293
- <div style="width: 250px;">
294
- <h4>{site_name}</h4>
295
- <p><b>Parameter:</b> {parameter}</p>
296
- <p><b>Value:</b> {value} {units}</p>
297
- <p><b>AQI:</b> {aqi} ({category})</p>
298
- <p><b>Coordinates:</b> {lat:.4f}, {lon:.4f}</p>
299
- <p><b>Time:</b> {item['DateObserved']} {item['HourObserved']}:00 GMT</p>
300
- <p><b>Station ID:</b> {item['AQSID']}</p>
301
- </div>
302
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
 
304
- # Color based on AQI
305
- if aqi <= 50:
306
- marker_color = 'green'
307
- elif aqi <= 100:
308
- marker_color = 'orange'
309
- elif aqi <= 150:
310
- marker_color = 'orange'
311
- elif aqi <= 200:
312
- marker_color = 'red'
313
- elif aqi <= 300:
314
- marker_color = 'purple'
 
 
 
 
 
 
 
315
  else:
316
- marker_color = 'darkred'
 
 
317
 
318
  # Add marker
319
  folium.Marker(
320
  [lat, lon],
321
  popup=folium.Popup(popup_content, max_width=300),
322
- tooltip=f"{site_name}: {parameter} = {value} {units} (AQI: {aqi})",
323
- icon=folium.Icon(color=marker_color, icon='cloud')
324
  ).add_to(m)
325
 
326
  except Exception as e:
@@ -329,16 +356,19 @@ class AccurateAirQualityMapper:
329
  # Add legend
330
  legend_html = """
331
  <div style="position: fixed;
332
- bottom: 50px; left: 50px; width: 180px; height: 200px;
333
  background-color: white; border:2px solid grey; z-index:9999;
334
- font-size:14px; padding: 10px">
335
- <h4>AQI Legend</h4>
 
336
  <p><i class="fa fa-circle" style="color:green"></i> Good (0-50)</p>
337
  <p><i class="fa fa-circle" style="color:orange"></i> Moderate (51-100)</p>
338
  <p><i class="fa fa-circle" style="color:orange"></i> Unhealthy for Sensitive (101-150)</p>
339
  <p><i class="fa fa-circle" style="color:red"></i> Unhealthy (151-200)</p>
340
  <p><i class="fa fa-circle" style="color:purple"></i> Very Unhealthy (201-300)</p>
341
  <p><i class="fa fa-circle" style="color:darkred"></i> Hazardous (301+)</p>
 
 
342
  </div>
343
  """
344
  m.get_root().html.add_child(folium.Element(legend_html))
@@ -352,13 +382,15 @@ class AccurateAirQualityMapper:
352
 
353
  table_data = []
354
  for item in data:
 
355
  table_data.append({
356
  'Site Name': item['SiteName'],
357
  'State': item['StateCode'],
358
  'Parameter': item['ParameterName'],
 
359
  'Value': item['Value'],
360
  'Units': item['ReportingUnits'],
361
- 'AQI': item['AQI'],
362
  'Category': item['Category']['Name'],
363
  'Latitude': round(item['Latitude'], 4),
364
  'Longitude': round(item['Longitude'], 4),
@@ -375,11 +407,30 @@ mapper = AccurateAirQualityMapper()
375
 
376
  def update_map():
377
  """Update map with accurate coordinates"""
378
- print("πŸš€ Starting accurate air quality mapping...")
379
 
380
  # Fetch data
381
  data, status = mapper.fetch_airnow_bulk_data()
382
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  # Create map
384
  map_html = mapper.create_map(data)
385
 
@@ -393,39 +444,40 @@ with gr.Blocks(title="Accurate AirNow Sensor Map", theme=gr.themes.Soft()) as de
393
 
394
  gr.Markdown(
395
  """
396
- # 🎯 Accurate AirNow Air Quality Map
397
 
398
- **βœ… PRECISE COORDINATES** - Uses EPA's official monitor coordinate database!
399
 
400
- This map displays real-time air quality data with **accurate station locations** by:
401
- 1. **Downloading EPA coordinates**: Gets precise lat/lon for every monitoring station
402
- 2. **Fetching AirNow bulk data**: Current hourly readings from 2,000+ stations
403
- 3. **Accurate mapping**: Stations plotted at their exact geographic locations
 
404
 
405
  ## Key Features:
406
- - 🎯 **Precise Locations**: EPA's official coordinate database
407
- - 🌍 **Complete Coverage**: All active AirNow monitoring stations
408
- - ⚑ **Real-time Data**: Latest hourly observations
409
- - πŸ“Š **Air Quality Focus**: OZONE, PM2.5, PM10, NO2, SO2, CO
410
- - πŸ”„ **Auto-updated**: Fresh data every hour
411
 
412
- **⚠️ Data Note**: This displays preliminary, real-time data for public information.
413
  For regulatory purposes, use EPA's official AQS data.
414
  """
415
  )
416
 
417
  with gr.Row():
418
- load_button = gr.Button("🎯 Load Accurate Air Quality Map", variant="primary", size="lg")
419
 
420
- status_text = gr.Markdown("Click the button above to load current air quality data with precise coordinates.")
421
 
422
  with gr.Tabs():
423
- with gr.TabItem("πŸ—ΊοΈ Accurate Map"):
424
- map_output = gr.HTML(label="Air Quality Map with Precise Coordinates")
425
 
426
- with gr.TabItem("πŸ“Š Station Data"):
427
  data_table = gr.Dataframe(
428
- label="Air Quality Monitoring Stations",
429
  interactive=False
430
  )
431
 
@@ -433,13 +485,17 @@ with gr.Blocks(title="Accurate AirNow Sensor Map", theme=gr.themes.Soft()) as de
433
  """
434
  ## Data Sources:
435
 
436
- **Coordinates**: EPA Air Quality System (AQS) - Official monitor locations
437
- **Air Quality Data**: AirNow hourly bulk files - Real-time observations
438
- **Coverage**: 2,000+ monitoring stations across US, Canada, and parts of Mexico
 
 
 
 
439
 
440
  ## Files Used:
441
- - `aqs_monitors.zip` - EPA monitor coordinates (364,377+ records)
442
- - `HourlyData_YYYYMMDDHH.dat` - AirNow real-time observations
443
 
444
  ## Links:
445
  - [EPA AQS Data](https://aqs.epa.gov/aqsweb/airdata/download_files.html)
 
225
  value = float(fields[7]) if fields[7].replace('.','').replace('-','').isdigit() else 0
226
  parameter = fields[5]
227
 
228
+ # Include ALL parameters (air quality + meteorological)
229
+ # Don't filter - the original successful run included everything
 
230
 
231
  aqi = self.calculate_aqi(parameter, value)
232
 
233
+ # Determine if it's an air quality or meteorological parameter
234
+ air_quality_params = ['OZONE', 'PM2.5', 'PM10', 'NO2', 'SO2', 'CO']
235
+ is_air_quality = parameter in air_quality_params
236
+
237
  record = {
238
  'DateObserved': fields[0],
239
  'HourObserved': fields[1],
 
246
  'Latitude': lat,
247
  'Longitude': lon,
248
  'AQI': aqi,
249
+ 'Category': {'Name': self.get_aqi_category(aqi) if is_air_quality else 'Meteorological'},
250
  'ReportingArea': fields[3],
251
+ 'StateCode': aqs_id[:2] if len(aqs_id) >= 2 else 'US',
252
+ 'IsAirQuality': is_air_quality
253
  }
254
 
255
  data.append(record)
 
292
  units = item['ReportingUnits']
293
  category = item['Category']['Name']
294
 
295
+ # Create popup content
296
+ if is_air_quality:
297
+ popup_content = f"""
298
+ <div style="width: 250px;">
299
+ <h4>{site_name} <span style="color: red;">🌬️ Air Quality</span></h4>
300
+ <p><b>Parameter:</b> {parameter}</p>
301
+ <p><b>Value:</b> {value} {units}</p>
302
+ <p><b>AQI:</b> {aqi} ({category})</p>
303
+ <p><b>Coordinates:</b> {lat:.4f}, {lon:.4f}</p>
304
+ <p><b>Time:</b> {item['DateObserved']} {item['HourObserved']}:00 GMT</p>
305
+ <p><b>Station ID:</b> {item['AQSID']}</p>
306
+ </div>
307
+ """
308
+ tooltip_text = f"{site_name}: {parameter} = {value} {units} (AQI: {aqi})"
309
+ else:
310
+ popup_content = f"""
311
+ <div style="width: 250px;">
312
+ <h4>{site_name} <span style="color: blue;">🌑️ Meteorological</span></h4>
313
+ <p><b>Parameter:</b> {parameter}</p>
314
+ <p><b>Value:</b> {value} {units}</p>
315
+ <p><b>Coordinates:</b> {lat:.4f}, {lon:.4f}</p>
316
+ <p><b>Time:</b> {item['DateObserved']} {item['HourObserved']}:00 GMT</p>
317
+ <p><b>Station ID:</b> {item['AQSID']}</p>
318
+ </div>
319
+ """
320
+ tooltip_text = f"{site_name}: {parameter} = {value} {units}"
321
 
322
+ # Determine marker appearance based on parameter type
323
+ is_air_quality = item.get('IsAirQuality', False)
324
+
325
+ if is_air_quality:
326
+ # Color based on AQI for air quality parameters
327
+ if aqi <= 50:
328
+ marker_color = 'green'
329
+ elif aqi <= 100:
330
+ marker_color = 'orange'
331
+ elif aqi <= 150:
332
+ marker_color = 'orange'
333
+ elif aqi <= 200:
334
+ marker_color = 'red'
335
+ elif aqi <= 300:
336
+ marker_color = 'purple'
337
+ else:
338
+ marker_color = 'darkred'
339
+ icon_type = 'cloud'
340
  else:
341
+ # Meteorological parameters use blue/gray
342
+ marker_color = 'blue'
343
+ icon_type = 'info-sign'
344
 
345
  # Add marker
346
  folium.Marker(
347
  [lat, lon],
348
  popup=folium.Popup(popup_content, max_width=300),
349
+ tooltip=tooltip_text,
350
+ icon=folium.Icon(color=marker_color, icon=icon_type)
351
  ).add_to(m)
352
 
353
  except Exception as e:
 
356
  # Add legend
357
  legend_html = """
358
  <div style="position: fixed;
359
+ bottom: 50px; left: 50px; width: 200px; height: 260px;
360
  background-color: white; border:2px solid grey; z-index:9999;
361
+ font-size:12px; padding: 10px">
362
+ <h4>Station Legend</h4>
363
+ <p><b>🌬️ Air Quality (AQI):</b></p>
364
  <p><i class="fa fa-circle" style="color:green"></i> Good (0-50)</p>
365
  <p><i class="fa fa-circle" style="color:orange"></i> Moderate (51-100)</p>
366
  <p><i class="fa fa-circle" style="color:orange"></i> Unhealthy for Sensitive (101-150)</p>
367
  <p><i class="fa fa-circle" style="color:red"></i> Unhealthy (151-200)</p>
368
  <p><i class="fa fa-circle" style="color:purple"></i> Very Unhealthy (201-300)</p>
369
  <p><i class="fa fa-circle" style="color:darkred"></i> Hazardous (301+)</p>
370
+ <p><b>🌑️ Meteorological:</b></p>
371
+ <p><i class="fa fa-circle" style="color:blue"></i> Weather Data</p>
372
  </div>
373
  """
374
  m.get_root().html.add_child(folium.Element(legend_html))
 
382
 
383
  table_data = []
384
  for item in data:
385
+ is_air_quality = item.get('IsAirQuality', False)
386
  table_data.append({
387
  'Site Name': item['SiteName'],
388
  'State': item['StateCode'],
389
  'Parameter': item['ParameterName'],
390
+ 'Type': '🌬️ Air Quality' if is_air_quality else '🌑️ Meteorological',
391
  'Value': item['Value'],
392
  'Units': item['ReportingUnits'],
393
+ 'AQI': item['AQI'] if is_air_quality else 'N/A',
394
  'Category': item['Category']['Name'],
395
  'Latitude': round(item['Latitude'], 4),
396
  'Longitude': round(item['Longitude'], 4),
 
407
 
408
  def update_map():
409
  """Update map with accurate coordinates"""
410
+ print("πŸš€ Starting comprehensive air quality and meteorological mapping...")
411
 
412
  # Fetch data
413
  data, status = mapper.fetch_airnow_bulk_data()
414
 
415
+ if data:
416
+ # Show parameter breakdown like the original
417
+ df_temp = pd.DataFrame(data)
418
+ param_counts = df_temp['ParameterName'].value_counts()
419
+
420
+ print(f"\nπŸ“ˆ Data Summary:")
421
+ print(f"Total stations: {len(df_temp)}")
422
+ print(f"Parameters monitored: {df_temp['ParameterName'].nunique()}")
423
+ print(f"Unique sites: {df_temp['SiteName'].nunique()}")
424
+
425
+ print(f"\nParameter breakdown:")
426
+ for param, count in param_counts.head(10).items():
427
+ print(f"{param}: {count}")
428
+
429
+ # Update status to include breakdown
430
+ air_quality_count = len([d for d in data if d.get('IsAirQuality', False)])
431
+ met_count = len(data) - air_quality_count
432
+ status = f"βœ… SUCCESS: {len(data)} total stations ({air_quality_count} air quality + {met_count} meteorological) from {len(set(d['SiteName'] for d in data))} unique sites"
433
+
434
  # Create map
435
  map_html = mapper.create_map(data)
436
 
 
444
 
445
  gr.Markdown(
446
  """
447
+ # 🎯 Complete AirNow Monitoring Network Map
448
 
449
+ **βœ… PRECISE COORDINATES + ALL STATIONS** - Every sensor with exact locations!
450
 
451
+ This map displays the **complete AirNow monitoring network** with accurate coordinates:
452
+ 1. **All Parameters**: Air quality (OZONE, PM2.5, PM10, NO2, SO2, CO) + Meteorological (TEMP, WIND, HUMIDITY, etc.)
453
+ 2. **EPA Coordinates**: Precise lat/lon for every monitoring station
454
+ 3. **Real-time Data**: Current hourly readings from 2,000+ stations
455
+ 4. **Visual Distinction**: 🌬️ Air quality (colored by AQI) vs 🌑️ Meteorological (blue)
456
 
457
  ## Key Features:
458
+ - 🎯 **All 7,000+ Sensors**: Complete monitoring network coverage
459
+ - πŸ“ **Exact Locations**: EPA's official coordinate database
460
+ - 🌬️ **Air Quality**: Color-coded by AQI health categories
461
+ - 🌑️ **Weather Data**: Temperature, wind, humidity, pressure
462
+ - ⚑ **Real-time**: Latest hourly observations
463
 
464
+ **⚠️ Data Note**: Real-time preliminary data for public information.
465
  For regulatory purposes, use EPA's official AQS data.
466
  """
467
  )
468
 
469
  with gr.Row():
470
+ load_button = gr.Button("🎯 Load Complete Monitoring Network", variant="primary", size="lg")
471
 
472
+ status_text = gr.Markdown("Click the button above to load ALL monitoring stations with precise coordinates.")
473
 
474
  with gr.Tabs():
475
+ with gr.TabItem("πŸ—ΊοΈ Complete Network Map"):
476
+ map_output = gr.HTML(label="Complete AirNow Monitoring Network with Precise Coordinates")
477
 
478
+ with gr.TabItem("πŸ“Š All Station Data"):
479
  data_table = gr.Dataframe(
480
+ label="All Monitoring Stations (Air Quality + Meteorological)",
481
  interactive=False
482
  )
483
 
 
485
  """
486
  ## Data Sources:
487
 
488
+ **Coordinates**: EPA Air Quality System (AQS) - Official monitor locations (364,377+ records)
489
+ **Monitoring Data**: AirNow hourly bulk files - Real-time observations from all sensors
490
+ **Coverage**: 7,000+ monitoring sensors across US, Canada, and parts of Mexico
491
+
492
+ ## Parameters Included:
493
+ **🌬️ Air Quality**: OZONE, PM2.5, PM10, NO2, SO2, CO (color-coded by AQI)
494
+ **🌑️ Meteorological**: TEMP, WIND, HUMIDITY, PRESSURE, SOLAR, PRECIP (blue markers)
495
 
496
  ## Files Used:
497
+ - `aqs_monitors.zip` - EPA monitor coordinates
498
+ - `HourlyData_YYYYMMDDHH.dat` - AirNow real-time observations (ALL parameters)
499
 
500
  ## Links:
501
  - [EPA AQS Data](https://aqs.epa.gov/aqsweb/airdata/download_files.html)