Upload 5 files
Browse files- README.md +14 -0
- app.py +56 -0
- geo_metals_model.joblib +3 -0
- model.py +21 -0
- requirements.txt +6 -0
README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: GeoMetals Predictor
|
| 3 |
+
emoji: 🧪
|
| 4 |
+
colorFrom: indigo
|
| 5 |
+
colorTo: red
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: "1.30.0"
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# GeoMetals Predictor
|
| 13 |
+
|
| 14 |
+
This app predicts concentrations of six heavy metals (Fe, Cr, Mn, Mo, In, Ta) based on user-selected locations in Metro Phoenix and stormwater runoff status using a Gradient Boosting Regressor model.
|
app.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app.py
|
| 2 |
+
import streamlit as st
|
| 3 |
+
import folium
|
| 4 |
+
from streamlit_folium import st_folium
|
| 5 |
+
from model import predict_metals
|
| 6 |
+
|
| 7 |
+
st.set_page_config(page_title="GeoMetals Predictor", layout="wide")
|
| 8 |
+
|
| 9 |
+
st.title("🧪 GeoMetals Exposure Predictor")
|
| 10 |
+
st.write("Click a location on the map and select stormwater runoff status to predict heavy metal concentrations.")
|
| 11 |
+
|
| 12 |
+
# Map setup
|
| 13 |
+
st.subheader("📍 Select Location")
|
| 14 |
+
m = folium.Map(location=[33.45, -112.07], zoom_start=10) # Metro Phoenix default center
|
| 15 |
+
|
| 16 |
+
marker = folium.LatLngPopup()
|
| 17 |
+
m.add_child(marker)
|
| 18 |
+
|
| 19 |
+
map_data = st_folium(m, width=700, height=500)
|
| 20 |
+
|
| 21 |
+
# User location input
|
| 22 |
+
lat, lon = None, None
|
| 23 |
+
if map_data["last_clicked"]:
|
| 24 |
+
lat = map_data["last_clicked"]["lat"]
|
| 25 |
+
lon = map_data["last_clicked"]["lng"]
|
| 26 |
+
st.success(f"Selected Coordinates: ({lat:.5f}, {lon:.5f})")
|
| 27 |
+
|
| 28 |
+
# Stormwater input
|
| 29 |
+
stormwater = st.radio("Stormwater Runoff Present?", ("Yes", "No")) == "Yes"
|
| 30 |
+
|
| 31 |
+
# Submit and predict
|
| 32 |
+
if st.button("🔍 Predict Heavy Metal Concentrations") and lat and lon:
|
| 33 |
+
prediction = predict_metals(lat, lon, stormwater)
|
| 34 |
+
|
| 35 |
+
st.subheader("🔬 Predicted Heavy Metal Concentrations (ppm)")
|
| 36 |
+
for metal, value in prediction.items():
|
| 37 |
+
warning = ""
|
| 38 |
+
if metal == "Fe_ppm" and value > 300:
|
| 39 |
+
warning = "⚠️ Above EPA soil screening level for Iron"
|
| 40 |
+
elif metal == "Cr_ppm" and value > 210:
|
| 41 |
+
warning = "⚠️ Chromium exceeds safe threshold"
|
| 42 |
+
elif metal == "Mn_ppm" and value > 500:
|
| 43 |
+
warning = "⚠️ High Manganese levels"
|
| 44 |
+
elif metal == "Mo_ppm" and value > 40:
|
| 45 |
+
warning = "⚠️ Elevated Molybdenum"
|
| 46 |
+
elif metal == "In_ppm" and value > 0.5:
|
| 47 |
+
warning = "⚠️ Indium concern"
|
| 48 |
+
elif metal == "Ta_ppm" and value > 1:
|
| 49 |
+
warning = "⚠️ Tantalum elevated"
|
| 50 |
+
st.write(f"**{metal.replace('_ppm', '')}: {value:.2f} ppm** {warning}")
|
| 51 |
+
|
| 52 |
+
st.subheader("📉 Legacy Risk Score (Experimental)")
|
| 53 |
+
score = sum(prediction.values()) / len(prediction)
|
| 54 |
+
st.metric("Aggregate Risk Score", f"{score:.2f}")
|
| 55 |
+
else:
|
| 56 |
+
st.info("Click a location on the map and hit 'Predict'.")
|
geo_metals_model.joblib
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c1e253e9cb2227de42757352b5e2976a85f35c09217c359ab0720963caed5902
|
| 3 |
+
size 513358
|
model.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# model.py
|
| 2 |
+
import joblib
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import numpy as np
|
| 5 |
+
from sklearn.ensemble import GradientBoostingRegressor
|
| 6 |
+
from sklearn.multioutput import MultiOutputRegressor
|
| 7 |
+
from sklearn.model_selection import train_test_split
|
| 8 |
+
|
| 9 |
+
MODEL_PATH = "geo_metals_model.joblib"
|
| 10 |
+
|
| 11 |
+
model = joblib.load(MODEL_PATH)
|
| 12 |
+
|
| 13 |
+
def predict_metals(lat, lon, stormwater):
|
| 14 |
+
input_data = pd.DataFrame([{
|
| 15 |
+
'Latitude': lat,
|
| 16 |
+
'Longitude': lon,
|
| 17 |
+
'Stormwater': 1 if stormwater else 0
|
| 18 |
+
}])
|
| 19 |
+
prediction = model.predict(input_data)[0]
|
| 20 |
+
metals = ['Fe_ppm', 'Cr_ppm', 'Mn_ppm', 'Mo_ppm', 'In_ppm', 'Ta_ppm']
|
| 21 |
+
return dict(zip(metals, prediction))
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.30.0
|
| 2 |
+
folium==0.15.1
|
| 3 |
+
streamlit-folium==0.11.1
|
| 4 |
+
scikit-learn==1.1.3
|
| 5 |
+
pandas
|
| 6 |
+
joblib
|