Skip to content

Latest commit

 

History

79 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Calvert City Odor Risk Forecaster

A daily-updating, publicly accessible odor risk forecast for Calvert City, KY and surrounding Marshall, McCracken, and Livingston counties. Powered by Open-Meteo weather data and logistic regression models trained on Pittsburgh community odor reports.

Live site: https://nwright083.github.io/weather-variable-analysis/ Plume model (companion tool): https://nwright083.github.io/Calvert-City-Plume-Analysis/


How it works

Every day at 1 AM Central (06:00 UTC), GitHub Actions runs generate_site.py, which:

  1. Fetches 16-day forecast, 30-day historical weather, and hourly data from Open-Meteo for 32 census tracts around Calvert City
  2. Writes raw weather features to docs/data/*.json
  3. Deploys the updated docs/ directory to GitHub Pages

The browser loads those JSON files and computes the ORI (Odor Risk Index) live using docs/model.js — a 0–100% logistic regression probability that conditions are favorable for odor transport from the industrial complex.


Repo structure

├── .github/workflows/forecast.yml   Daily CI pipeline (runs generate_site.py → deploys Pages)
│
├── docs/                            Static website (served by GitHub Pages)
│   ├── index.html                   Single-page app shell + tab layout
│   ├── app.js                       All UI logic: map, forecast grid, hourly chart, methodology
│   ├── model.js                     ORI computation mirroring odor_forecast_core.py (runs in browser)
│   ├── style.css                    All styles
│   └── data/                        Generated by CI each run — NOT committed to git
│       ├── forecast.json            16-day daily features per tract
│       ├── historical.json          30-day historical features per tract
│       ├── hourly.json              Per-hour features for Hourly tab
│       └── meta.json                Model coefficients, labels, metrics, UI config
│
├── generate_site.py                 CI entry point — fetches weather, builds JSON payloads
├── odor_forecast_core.py            Model coefficients + ORI math + Open-Meteo API calls
├── model_metrics.json               F1-optimal thresholds and performance stats per model
├── model_coeffs_hourly.json         Hourly case-crossover model coefficients
│
├── calvert_tracts.geojson           Census tract boundaries (32 tracts, used for map)
├── calvert_zips.geojson             ZIP code boundaries (fallback if tracts unavailable)
│
├── analyze_calvert_reports.py       Tool: fit a Calvert-specific model from local odor reports
│
├── ad_config.py                     Ad trigger configuration (thresholds, timing, provider)
├── ad_trigger.py                    ORI-triggered ad campaign decision engine
├── ad_providers/                    Pluggable ad provider adapters
│   ├── base.py                      Abstract provider interface
│   ├── mock.py                      Mock provider (logging only, for testing)
│   └── eltoro.py                    El Toro GeoFraming placeholder (pending API access)
│
└── scratch/                         Unit tests — run with: python -m pytest scratch/ -q
    ├── test_forecast_engine.py      Tests ORI math, wind alignment, distance decay
    ├── test_generate_site.py        Tests JSON payload building + NaN safety
    ├── test_js_model.py             Tests browser model.js matches Python via Node
    └── test_ad_trigger.py           Tests ad trigger framework

The production models

Mode Description Best for
Exact Pittsburgh (default) Raw Pittsburgh model, no spatial terms Uniform risk estimate across all tracts
Pittsburgh Proximity-Enhanced Pittsburgh logistic regression + wind direction alignment + distance-decay from source General use; accounts for where you are relative to the industrial complex
Calvert Fitted (auto-appears) Data-fitted from real Calvert odor reports Most accurate once enough local reports exist (see below)

ORI formula

z = α + β₁·Temp + β₂·Temp² + β₃·Solar + β₄·RH + β₅·WindSpeed
      + β₆·Precip + β₇·DTR + β₈·BLH + β₉·Pressure
      [+ β₁₀·Exposure + β₁₁·WindAlignment  ← proximity model only]

ORI = 100% / (1 + e^−z)

Coefficients are in odor_forecast_core.py. Alert fires when ORI exceeds the F1-optimal threshold stored in model_metrics.json.


Setup & local development

Prerequisites: Python 3.11+, Node.js (for JS tests only)

# Clone and install dependencies
git clone https://github.com/nwright083/weather-variable-analysis.git
cd weather-variable-analysis
python -m venv .venv && source .venv/bin/activate
pip install requests pandas numpy

# Generate data locally and preview
python generate_site.py
python -m http.server 8765 --directory docs
# Open http://localhost:8765/

# Run tests
python -m pytest scratch/ -q

CI / GitHub Actions

The workflow (.github/workflows/forecast.yml) triggers on:

  • Daily schedule: 06:00 UTC
  • Push to main: any commit triggers a redeploy
  • Manual: Actions tab → "Daily Odor Forecast" → Run workflow

If Open-Meteo is unreachable, the script exits non-zero and the last good deployment stays live.

Important: Do not push broken code to main — every push redeploys the live site.


Fitting a local Calvert model

Once you have collected enough real odor reports (aim for 50+ with yes/no confirmed detections), run:

python analyze_calvert_reports.py

This compares local conditions against the Pittsburgh-trained coefficients and, if the local data is strong enough, generates and installs calvert_fitted_model.json. The next time generate_site.py runs it will automatically expose the new model in the dashboard.

Reports can come from:

  • The Smell My City app (smellmycity.org) — preferred
  • A Google Form linked from the "Report Odor" tab — pre-fills lat/lon from the user's browser

Research archive

The Pittsburgh and Louisville analysis notebooks and scripts used to develop and validate the logistic regression models are preserved in the private Gitea mirror (not included in this public GitHub repo). They are reference material showing the scientific methodology behind the deployed coefficients.

Key file: Pittsburgh Data/Dual_Model_Proximity_Analysis.py (in Gitea) — this is the script that produced the deployed coefficients.


About

Weather variable analysis project for smell reports in Louisville and Pittsburgh.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages