修复跑道数据:用 LIKE 查询所有 RKSI_RWY_%,不再硬编码索引

This commit is contained in:
2569718930@qq.com
2026-05-13 22:00:00 +08:00
parent 7038b14904
commit 26fff84c3a
2 changed files with 46 additions and 19 deletions
+31
View File
@@ -25,6 +25,37 @@ pub fn get_daily_max(db_path: &str, city_key: &str) -> Option<(f64, Option<Strin
.ok()
}
/// Get runway-level observations for Seoul/Busan (stored as ICAO_RWY_N).
pub fn get_runway_temps(db_path: &str, icao: &str) -> Vec<(String, f64)> {
let conn = match Connection::open(db_path) {
Ok(c) => c,
Err(_) => return vec![],
};
let pattern = format!("{}_RWY_%", icao.to_uppercase());
let mut stmt = match conn.prepare(
"SELECT icao, temp_c FROM airport_obs_log \
WHERE icao LIKE ?1 AND created_at > datetime('now', '-120 minutes') \
ORDER BY icao, created_at DESC"
) {
Ok(s) => s,
Err(_) => return vec![],
};
let mut results: Vec<(String, f64)> = vec![];
let rows = stmt.query_map(rusqlite::params![pattern], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, f64>(1)?))
});
if let Ok(iter) = rows {
let mut seen = std::collections::HashSet::new();
for r in iter.filter_map(|r| r.ok()) {
// Take only the first (most recent) per unique icao
if seen.insert(r.0.clone()) {
results.push(r);
}
}
}
results
}
/// Get recent temperature observations for an ICAO station.
pub fn get_recent_obs(db_path: &str, icao: &str, minutes: i32, limit: usize) -> Vec<ObsRow> {
let conn = match Connection::open(db_path) {
+15 -19
View File
@@ -32,12 +32,6 @@ const CITIES: &[(&str, &str, &str, &str, &str, i32, f64, &str, usize)] = &[
("taipei", "台北", "Taipei", "466920", "Songshan", 8, 1.5, "TST", 0),
];
// 跑道标签(与 AMOS scrape 返回的一致)
const RUNWAY_LABELS: &[&[&str]] = &[
&["18L/36R", "18R/36L"], // seoul
&["18L/36R", "18R/36L"], // busan
];
// ── app state ──
struct AppState {
db_path: String,
@@ -83,7 +77,7 @@ fn parse_obs_time(raw: &str) -> Option<chrono::DateTime<Utc>> {
// ── data loading ──
fn load_city_snapshot(db_path: &str, idx: usize, (key, _zh, en, icao, airport, tz, thresh, tz_abbr, rw_count): &(&str, &str, &str, &str, &str, i32, f64, &str, usize)) -> CitySnapshot {
fn load_city_snapshot(db_path: &str, _idx: usize, (key, _zh, en, icao, airport, tz, thresh, tz_abbr, rw_count): &(&str, &str, &str, &str, &str, i32, f64, &str, usize)) -> CitySnapshot {
let now_utc = Utc::now();
let offset = chrono::FixedOffset::east_opt(*tz * 3600).unwrap();
@@ -139,18 +133,20 @@ fn load_city_snapshot(db_path: &str, idx: usize, (key, _zh, en, icao, airport, t
_ => false,
};
// Runway data: query each runway ICAO
let mut runway_pairs: Vec<(String, f64)> = vec![];
if *rw_count > 0 && idx < RUNWAY_LABELS.len() {
for i in 0..*rw_count {
let rw_icao = format!("{}_RWY_{}", icao, i);
let rw_obs = db::get_recent_obs(db_path, &rw_icao, 120, 1);
if let Some(rw_temp) = rw_obs.first().and_then(|o| o.temp_c) {
let label = RUNWAY_LABELS[idx].get(i).unwrap_or(&"?").to_string();
runway_pairs.push((label, rw_temp));
}
}
}
// Runway data: query all ICAO_RWY_* for this airport
let runway_pairs: Vec<(String, f64)> = if *rw_count > 0 {
db::get_runway_temps(db_path, icao)
.into_iter()
.map(|(rw_icao, temp)| {
let label = rw_icao.strip_prefix(&format!("{}_RWY_", icao.to_uppercase()))
.map(|n| format!("RWY {}", n))
.unwrap_or_else(|| rw_icao);
(label, temp)
})
.collect()
} else {
vec![]
};
CitySnapshot {
name: key.to_string(),