修复跑道数据:用 LIKE 查询所有 RKSI_RWY_%,不再硬编码索引
This commit is contained in:
@@ -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
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user