diff --git a/Cargo.lock b/Cargo.lock index b2c05ea..a27552a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,15 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "approx" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" +dependencies = [ + "num-traits", +] + [[package]] name = "arrayvec" version = "0.7.4" @@ -360,6 +369,18 @@ dependencies = [ "serde", ] +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -434,10 +455,14 @@ dependencies = [ "base64", "bytes", "chrono", + "country-boundaries", "futures", "image", + "isocountry", + "radians", "regex", "reqwest", + "s2", "serde", "serde_json", "sha256", @@ -460,6 +485,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cgmath" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317" +dependencies = [ + "approx", + "num-traits", +] + [[package]] name = "chrono" version = "0.4.31" @@ -524,6 +559,12 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "country-boundaries" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a949524fd9d83cd48e5046cd6212c1e533477d376b82570b6c0bcc4c6775625" + [[package]] name = "cpufeatures" version = "0.2.11" @@ -827,6 +868,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float_extras" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22b70f8649ea2315955f1a36d964b0e4da482dfaa5f0d04df0d1fb7c338ab7a" +dependencies = [ + "libc", +] + [[package]] name = "flume" version = "0.11.0" @@ -1447,6 +1497,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "isocountry" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ea1dc4bf0fb4904ba83ffdb98af3d9c325274e92e6e295e4151e86c96363e04" +dependencies = [ + "serde", + "thiserror", +] + [[package]] name = "itertools" version = "0.10.5" @@ -2233,6 +2293,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radians" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cbdc3e524e4d0d8f8e962046fa011ab2beaba74269f8d4aa6f61dc900b22568" +dependencies = [ + "real_float", +] + [[package]] name = "rand" version = "0.8.5" @@ -2283,6 +2352,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "real_float" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dee308ef52b7603c018d9a0c2ff03fe65fb064bb8605a8c7ac99fb2dfb7542f" + [[package]] name = "redox_syscall" version = "0.4.1" @@ -2462,6 +2537,20 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "s2" +version = "0.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7fbc04bb52c40b5f48c9bb2d2961375301916e0c25d9f373750654d588cd5c" +dependencies = [ + "bigdecimal", + "cgmath", + "float_extras", + "lazy_static", + "libm", + "serde", +] + [[package]] name = "schannel" version = "0.1.22" diff --git a/Cargo.toml b/Cargo.toml index 518768e..600d986 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,14 @@ axum-extra = { version = "0.9.0", features = ["typed-header"] } base64 = "0.21.5" bytes = "1.5.0" chrono = { version = "0.4.31", default-features = false, features = ["std"] } +country-boundaries = "1.2.0" futures = "0.3.29" image = "0.24.7" +isocountry = "0.3.2" +radians = "0.3.1" regex = "1.10.2" reqwest = { version = "0.11.22", features = ["json"] } +s2 = "0.0.12" serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" sha256 = "1.4.0" diff --git a/src/cal/day21.rs b/src/cal/day21.rs new file mode 100644 index 0000000..28fe6a6 --- /dev/null +++ b/src/cal/day21.rs @@ -0,0 +1,55 @@ +use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing::get, Router}; +use country_boundaries::{CountryBoundaries, LatLon, BOUNDARIES_ODBL_360X180}; +use s2::{cellid::CellID, latlng::LatLng}; + +pub(crate) fn router() -> Router { + Router::new() + .route("/21/coords/:id", get(coords)) + .route("/21/country/:id", get(country)) +} + +async fn coords(Path(id): Path) -> Result { + let id = u64::from_str_radix(&id, 2).map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?; + let cell = CellID(id); + if !cell.is_valid() { + return Err((StatusCode::BAD_REQUEST, "Invalid cell ID".to_string())); + } + let pos = LatLng::from(cell); + let lat = format!("{:.3}", radians::Deg::new(pos.lat.deg())); + let lat = if let Some(lat) = lat.strip_prefix('-') { + format!("{lat}S") + } else { + lat + "N" + }; + let lng = format!("{:.3}", radians::Deg::new(pos.lng.deg())); + let lng = if let Some(lng) = lng.strip_prefix('-') { + format!("{lng}W") + } else { + lng + "E" + }; + Ok(lat + " " + &lng) +} + +async fn country(Path(id): Path) -> Result { + let id = u64::from_str_radix(&id, 2).map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?; + let cell = CellID(id); + if !cell.is_valid() { + return Err((StatusCode::BAD_REQUEST, "Invalid cell ID".to_string())); + } + + let pos = LatLng::from(cell); + let pos = LatLon::new(pos.lat.deg(), pos.lng.deg()) + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + + let countries = CountryBoundaries::from_reader(BOUNDARIES_ODBL_360X180) + .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; + + Ok(countries + .ids(pos) + .into_iter() + .find_map(|id| isocountry::CountryCode::for_alpha2(id).ok()) + .ok_or((StatusCode::INTERNAL_SERVER_ERROR, "Not found".to_string()))? + .name() + .trim_end_matches(" Darussalam") + .to_string()) +} diff --git a/src/cal/mod.rs b/src/cal/mod.rs index 3d677a6..31c0f2b 100644 --- a/src/cal/mod.rs +++ b/src/cal/mod.rs @@ -13,6 +13,7 @@ mod day15; mod day18; mod day19; mod day20; +mod day21; pub(crate) fn router(pool: sqlx::PgPool) -> axum::Router { axum::Router::new() @@ -31,4 +32,5 @@ pub(crate) fn router(pool: sqlx::PgPool) -> axum::Router { .nest("/", day18::router(pool)) .nest("/", day19::router()) .nest("/", day20::router()) + .nest("/", day21::router()) }