implement phase 5~7

This commit is contained in:
ms2k 2026-03-31 22:28:07 +09:00
commit 6140548774

77
src/projection.rs Normal file
View file

@ -0,0 +1,77 @@
use eframe::egui;
pub trait MapProjection: Send + Sync {
fn project(&self, lon: f64, lat: f64, rect: egui::Rect) -> egui::Pos2;
fn unproject(&self, pos: egui::Pos2, rect: egui::Rect) -> (f64, f64);
fn name(&self) -> &'static str;
}
pub struct Equirectangular;
impl MapProjection for Equirectangular {
fn project(&self, lon: f64, lat: f64, rect: egui::Rect) -> egui::Pos2 {
egui::pos2(
rect.left() + ((lon + 180.0) / 360.0) as f32 * rect.width(),
rect.top() + ((90.0 - lat) / 180.0) as f32 * rect.height(),
)
}
fn unproject(&self, pos: egui::Pos2, rect: egui::Rect) -> (f64, f64) {
let lon = (pos.x - rect.left()) as f64 / rect.width() as f64 * 360.0 - 180.0;
let lat = 90.0 - (pos.y - rect.top()) as f64 / rect.height() as f64 * 180.0;
(lon, lat)
}
fn name(&self) -> &'static str {
"Equirectangular"
}
}
#[cfg(test)]
mod tests {
use super::*;
fn unit_rect() -> egui::Rect {
egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(360.0, 180.0))
}
#[test]
fn roundtrip_center() {
let proj = Equirectangular;
let r = unit_rect();
let pos = proj.project(0.0, 0.0, r);
let (lon, lat) = proj.unproject(pos, r);
assert!((lon).abs() < 1e-4);
assert!((lat).abs() < 1e-4);
}
#[test]
fn roundtrip_corners() {
let proj = Equirectangular;
let r = unit_rect();
for (lon, lat) in [(-180.0_f64, 90.0_f64), (180.0, -90.0), (-180.0, -90.0), (180.0, 90.0)] {
let pos = proj.project(lon, lat, r);
let (lon2, lat2) = proj.unproject(pos, r);
assert!((lon - lon2).abs() < 1e-4, "lon {lon} → {lon2}");
assert!((lat - lat2).abs() < 1e-4, "lat {lat} → {lat2}");
}
}
#[test]
fn project_prime_meridian_equator() {
let proj = Equirectangular;
let r = unit_rect();
let pos = proj.project(0.0, 0.0, r);
assert!((pos.x - 180.0).abs() < 1e-3, "x={}", pos.x);
assert!((pos.y - 90.0).abs() < 1e-3, "y={}", pos.y);
}
#[test]
fn rect_scaling() {
let proj = Equirectangular;
let r = egui::Rect::from_min_size(egui::pos2(10.0, 20.0), egui::vec2(720.0, 360.0));
let pos = proj.project(0.0, 0.0, r);
assert!((pos.x - 370.0).abs() < 1e-3, "x={}", pos.x);
assert!((pos.y - 200.0).abs() < 1e-3, "y={}", pos.y);
}
}