feat: introduce PolyWeather application with an interactive map, detailed city weather, trend analysis, and a supporting API.
This commit is contained in:
+16
-2
@@ -284,7 +284,7 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
|
||||
# ── 10. Shared analysis (probability, trend, AI) via trend_engine ──
|
||||
# This single call replaces the duplicate probability engine, dead market
|
||||
# detection, forecast bust grading, and AI context building.
|
||||
from src.analysis.trend_engine import analyze_weather_trend as _trend_analyze
|
||||
from src.analysis.trend_engine import analyze_weather_trend as _trend_analyze, calculate_prob_distribution
|
||||
from src.analysis.ai_analyzer import get_ai_analysis
|
||||
|
||||
probabilities = []
|
||||
@@ -434,19 +434,33 @@ def _analyze(city: str, force_refresh: bool = False) -> Dict[str, Any]:
|
||||
day_m["MGM"] = _sf(mgm_daily[d_str])
|
||||
|
||||
d_val, d_winfo = None, ""
|
||||
d_probs = []
|
||||
if day_m:
|
||||
try:
|
||||
blended, winfo = calculate_dynamic_weights(city, day_m)
|
||||
if blended is not None:
|
||||
d_val = blended
|
||||
d_winfo = winfo
|
||||
|
||||
# Calculate future probability based on model divergence
|
||||
m_vals = [v for v in day_m.values() if v is not None]
|
||||
if len(m_vals) > 1:
|
||||
# Use spread as a proxy for sigma.
|
||||
# sigma = (max-min)/2 with a floor of 0.6
|
||||
d_sigma = max(0.6, (max(m_vals) - min(m_vals)) / 2.0)
|
||||
else:
|
||||
d_sigma = 1.0
|
||||
|
||||
prob_obj = calculate_prob_distribution(d_val, d_sigma, None, sym)
|
||||
d_probs = prob_obj.get("probabilities", [])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if day_m:
|
||||
multi_model_daily[d_str] = {
|
||||
"models": day_m,
|
||||
"deb": {"prediction": d_val, "weights_info": d_winfo}
|
||||
"deb": {"prediction": d_val, "weights_info": d_winfo},
|
||||
"probabilities": d_probs if i > 0 else probabilities # Use today's real prob for today
|
||||
}
|
||||
|
||||
# ── Assemble result ──
|
||||
|
||||
+13
-3
@@ -818,8 +818,18 @@ function renderChart(data) {
|
||||
|
||||
function renderProbabilities(data) {
|
||||
const container = document.getElementById("probBars");
|
||||
const probs = data.probabilities?.distribution || [];
|
||||
const mu = data.probabilities?.mu;
|
||||
const targetDate = selectedForecastDate || data.local_date;
|
||||
|
||||
let probs = [];
|
||||
let mu = null;
|
||||
|
||||
if (targetDate === data.local_date) {
|
||||
probs = data.probabilities?.distribution || [];
|
||||
mu = data.probabilities?.mu;
|
||||
} else if (data.multi_model_daily && data.multi_model_daily[targetDate]) {
|
||||
probs = data.multi_model_daily[targetDate].probabilities || [];
|
||||
mu = data.multi_model_daily[targetDate].deb?.prediction;
|
||||
}
|
||||
|
||||
if (probs.length === 0) {
|
||||
container.innerHTML =
|
||||
@@ -834,7 +844,6 @@ function renderProbabilities(data) {
|
||||
|
||||
probs.forEach((p, i) => {
|
||||
const pct = Math.round(p.probability * 100);
|
||||
const width = Math.max(pct, 8);
|
||||
html += `
|
||||
<div class="prob-row">
|
||||
<div class="prob-label">${p.value}${data.temp_symbol}</div>
|
||||
@@ -962,6 +971,7 @@ function switchForecastDate(cityName, dateStr) {
|
||||
const data = cityDataCache[cityName];
|
||||
if (data) {
|
||||
renderModels(data);
|
||||
renderProbabilities(data);
|
||||
renderForecast(data);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user