A decade of air quality data across 31 Indian states, matched against disease mortality records — revealing how particulate pollution drives death at a measurable, statistically significant scale.
Vāyu is a full-stack research platform that combines historical air pollution data, real-time AQI monitoring, soil and water quality metrics, and ML-powered forecasting to visualize the relationship between environmental pollution and disease mortality across India (2013–2023).
The platform covers 200+ Indian cities and is built as a three-service architecture: a static frontend, a Node.js API backend, and a Python ML microservice.
- Live AQI Map — Real-time air quality index from the CPCB API, rendered on an interactive Leaflet map with city-level markers and color-coded AQI bands
- Historical Trends — PM2.5, AQI, and pollutant trends from 2013–2023 across Indian states and cities
- Pollution × Mortality Correlations — Statistical analysis linking particulate pollution to disease-specific death rates (GBD dataset)
- ML Forecasting — Random Forest model predicting future PM2.5 levels using lag features and weather covariates
- Multi-metric Toggle — Switch between Air Quality (AQI), Soil Pollution, Water Quality, and Noise Pollution layers
- City Comparison Nodes — Drag-and-drop comparison of up to N cities side by side on the map
- Research Dashboard — Curated findings, regression results, and phase-by-phase analysis output
- Deaths Modal — Estimated mortality attributable to pollution per city/state
┌─────────────────────────────────────────────────┐
│ Frontend (HTML/CSS/JS) │
│ Leaflet · Chart.js · Vanilla JS modules │
│ Deployed: Vercel │
└──────────────────┬──────────────────────────────┘
│ REST API calls
┌──────────────────▼──────────────────────────────┐
│ Backend (Node.js / Express) │
│ Routes: /api/past · /api/present · /api/future │
│ /api/water · /api/soil │
│ Deployed: Render │
└──────────────────┬──────────────────────────────┘
│ HTTP (port 5001)
┌──────────────────▼──────────────────────────────┐
│ ML Service (Python / Flask) │
│ Random Forest · scikit-learn · StandardScaler │
│ Deployed: Render │
└─────────────────────────────────────────────────┘
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/past/city/:city |
Historical AQI & pollutant data |
| GET | /api/present/city/:city |
Live AQI from CPCB API |
| GET | /api/future/city/:city |
ML-predicted AQI (next period) |
| GET | /api/water |
Water quality data by station |
| GET | /api/soil |
Soil pollution data by district |
| GET | /api/health |
Backend health check |
| Layer | Technology |
|---|---|
| Frontend | HTML5, CSS3, Vanilla JS (ES modules) |
| Maps | Leaflet.js |
| Charts | Chart.js 4 |
| Backend | Node.js ≥ 18, Express 4, node-fetch, dotenv |
| ML Service | Python 3.11, Flask, scikit-learn 1.5.2, pandas, numpy |
| ML Model | Random Forest Regressor + StandardScaler |
| Data | CPCB API (live AQI), GBD dataset (mortality), custom CSVs |
| Containerization | Docker + Docker Compose |
| Deployment | Vercel (Frontend), Render (Backend + ML) |
Pollution_Project-main/
├── FrontEnd/ # Static frontend
│ ├── index.html # Research dashboard & hero
│ ├── map.html # Live AQI map
│ ├── research.html # Detailed research findings
│ ├── js/
│ │ ├── app.js # Entry point, map init, module boot
│ │ ├── aqi-engine.js # AQI calculation logic
│ │ ├── charts.js # Chart.js wrappers
│ │ ├── data-loader.js # API fetch orchestration
│ │ ├── ml-section.js # ML forecast UI
│ │ ├── node-manager.js # City comparison nodes
│ │ ├── popups.js # Map popup rendering
│ │ ├── search.js # City search
│ │ ├── soil-loader.js # Soil layer
│ │ ├── water-loader.js # Water layer
│ │ └── deaths-modal.js # Mortality modal
│ ├── styles/ # Modular CSS
│ ├── nginx.conf # Nginx config for Docker
│ └── vercel.json # Vercel routing config
│
├── Backend/ # Node.js API
│ ├── server.js # Express app entry point
│ ├── routes/
│ │ ├── analysis.js # Past/present/future AQI routes
│ │ ├── water.js # Water quality routes
│ │ └── soil.js # Soil pollution routes
│ ├── data/ # Pre-processed JSON data files
│ │ ├── city_data.json
│ │ ├── city_coords.json
│ │ ├── corr_data.json
│ │ ├── meta_data.json
│ │ ├── soil_data.json
│ │ └── water_quality.json
│ ├── package.json
│ └── Dockerfile
│
├── ml_model/ # Python ML microservice
│ ├── ml_service.py # Flask prediction server
│ ├── save_models.py # Re-train & save model artifacts
│ ├── models/
│ │ ├── rf_model.pkl # Trained Random Forest
│ │ └── scaler.pkl # Fitted StandardScaler
│ ├── requirements.txt
│ └── Dockerfile
│
├── notebooks/ # Jupyter analysis pipeline
│ ├── Phase0.ipynb # Data ingestion & column classification
│ ├── Phase0.5.ipynb # Data cleaning
│ ├── Phase1.ipynb # Air pollution EDA
│ ├── phase_2_disease_clean.ipynb # Disease mortality cleaning
│ ├── phase_3_join.ipynb # Merging pollution + mortality
│ ├── phase_4_aqi.ipynb # AQI computation
│ └── phase_5_ml.ipynb # Model training & evaluation
│
├── outputs/ # Pipeline output CSVs
│ ├── air_pollution_master.csv
│ ├── air_pollution_with_aqi.csv
│ ├── combined_master.csv
│ ├── disease_master.csv
│ ├── final_dataset/ # Per-city CSV files (200+ cities)
│ ├── phase5_model_summary.csv
│ └── taskb_correlation_results.csv
│
├── docker-compose.yml # Local multi-service orchestration
├── render.yaml # Render deployment config
├── env.example # Environment variable template
└── DEPLOY.md # Full deployment guide
The research data was built through 6 phases of Jupyter notebook processing:
| Phase | Notebook | Description |
|---|---|---|
| 0 | Phase0.ipynb |
Raw data ingestion, column classification |
| 0.5 | Phase0.5.ipynb |
Data cleaning, outlier removal |
| 1 | Phase1.ipynb |
Air pollution EDA, PM2.5 trend analysis |
| 2 | phase_2_disease_clean.ipynb |
GBD disease mortality cleaning |
| 3 | phase_3_join.ipynb |
Joining pollution + mortality datasets |
| 4 | phase_4_aqi.ipynb |
AQI index computation (CPCB methodology) |
| 5 | phase_5_ml.ipynb |
Random Forest training, evaluation, export |
The ML service exposes a /predict endpoint called by the Node backend for the /api/future/:city route.
Model: Random Forest Regressor
Target: PM2.5 concentration (next period)
Features: PM2.5 lag-1, PM2.5 lag-2, temperature, relative humidity, wind speed, rainfall
Preprocessing: StandardScaler (fitted on training set, serialized as scaler.pkl)
Note:
rf_model.pklandscaler.pklare tied to scikit-learn 1.5.2. Do not upgrade scikit-learn without retraining and re-saving the models.
To retrain the model from scratch:
cd ml_model
python save_models.pyYou need three terminals running simultaneously.
- Node.js ≥ 18
- Python ≥ 3.11
- Git
cd ml_model
pip install -r requirements.txt
python ml_service.py
# Starts on http://localhost:5001
# Health check: http://localhost:5001/healthcd Backend
npm install
cp ../env.example .env # first time only — fill in your CPCB_API_KEY
node server.js
# Starts on http://localhost:5000
# Health check: http://localhost:5000/api/healthcd FrontEnd
# Option A: VS Code Live Server (recommended — auto-reloads)
# Right-click index.html → "Open with Live Server"
# Option B: Python
python -m http.server 5500
# Open http://localhost:5500The frontend reads from http://localhost:5000/api by default — no config changes needed.
To run all three services with Docker Compose:
# Copy and fill in environment variables
cp env.example .env
# Build and start all containers
docker compose up -d
# Check status
docker compose ps
# View logs
docker compose logs -f
# Stop everything
docker compose downServices and their internal hostnames:
| Container | Hostname | Port |
|---|---|---|
vayu_ml_service |
ml_service |
5001 |
vayu_backend |
backend |
5000 |
vayu_frontend |
frontend |
80 |
Copy env.example to Backend/.env and fill in the values:
PORT=5000
NODE_ENV=production
# Internal URL of the ML service
ML_SERVICE_URL=http://ml_service:5001
# Allowed CORS origin for the frontend
FRONTEND_URL=http://localhost
# CPCB Air Quality API key from data.gov.in
# Get one at: https://data.gov.in/resource/real-time-air-quality-index-various-locations
CPCB_API_KEY=your_cpcb_api_key_hereNever commit your actual API key. Only put it in your local
.envor as a secret environment variable in your hosting provider.
The project is configured for Vercel (frontend) + Render (backend + ML). Follow the order below — services depend on each other's URLs.
Step 1: Push all files to GitHub.
Step 2: Deploy the ML service on Render first.
Render auto-detects render.yaml. Deploy vayu-ml-service and note the assigned URL (e.g., https://vayu-ml-service.onrender.com). Test: GET /health should return { "model_loaded": true }.
Step 3: Deploy the backend on Render.
Set these environment variables in the Render dashboard:
| Variable | Value |
|---|---|
ML_SERVICE_URL |
URL from Step 2 |
FRONTEND_URL |
Your Vercel URL (set after Step 4) |
CPCB_API_KEY |
Your key from data.gov.in |
Step 4: Deploy the frontend on Vercel.
Connect your GitHub repo. Vercel reads FrontEnd/vercel.json automatically. Set the VAYU_API_BASE to your Render backend URL.
Full step-by-step instructions are in DEPLOY.md.
| AQI Range | Category | Color |
|---|---|---|
| 0–50 | Good | 🟢 #a8e063 |
| 51–100 | Satisfactory | 🟡 #fdd835 |
| 101–200 | Moderate | 🟠 #ff7c00 |
| 201–300 | Poor | 🔴 #f50057 |
| 301–400 | Very Poor | 🟣 #9c27b0 |
| 401+ | Severe | ⚫ #6a0080 |
- Air Quality: Central Pollution Control Board (CPCB), India — data.gov.in
- Disease Mortality: Global Burden of Disease (GBD) Study
- Soil & Water: Processed from government monitoring datasets
- City Coordinates: Custom curated
city_coords.json
This project was built for research and educational purposes. See individual data source licenses for data usage terms.