diff --git a/CHANGELOG.md b/CHANGELOG.md index fb8f1ea6..f9aa35d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Added +- New `equal_earth` map projection (`+proj=eqearth`). + - New caching layer that wraps any `Reader` with an in-memory, writeable cache backend (currently duckdb or sqlite), making write-constrained databases usable and avoiding repeated remote reads during interactive iteration. diff --git a/doc/syntax/coord/crs.qmd b/doc/syntax/coord/crs.qmd index 1d2b34c9..e47952fc 100644 --- a/doc/syntax/coord/crs.qmd +++ b/doc/syntax/coord/crs.qmd @@ -101,6 +101,7 @@ More information about them can be found on the [PRØJ website](https://proj.org | Azimuthal Equidistant | `azimuthal_equidistant` | (0, 0) | NA | `+proj=aeqd` | | Interrupted Goode Homolosine | `igh` | 0 | NA | `+proj=igh` | | Robinson | `robinson` | 0 | NA | `+proj=robin` | +| Equal Earth | `equal_earth` | 0 | NA | `+proj=eqearth` | The way to draw an unsupported map is to use the `target` setting. Unsupported maps are likely drawn without projection-appropriate clipping. diff --git a/src/plot/projection/coord/map_projections.rs b/src/plot/projection/coord/map_projections.rs index 365f47ed..7489a0cd 100644 --- a/src/plot/projection/coord/map_projections.rs +++ b/src/plot/projection/coord/map_projections.rs @@ -37,6 +37,7 @@ pub const NAMED_PROJECTIONS: &[&str] = &[ "azimuthal_equidistant", "igh", "robinson", + "equal_earth", ]; // ============================================================================= @@ -338,6 +339,7 @@ macro_rules! build_projection { "eqc" | "equirectangular" => Arc::new(Equirectangular { lon_0 }), "cea" | "equal_area" => Arc::new(CylindricalEqualArea { lon_0 }), "robin" | "robinson" => Arc::new(Robinson { lon_0 }), + "eqearth" | "equal_earth" => Arc::new(EqualEarth { lon_0 }), "moll" | "mollweide" => Arc::new(Mollweide { lon_0 }), "sinu" | "sinusoidal" => Arc::new(Sinusoidal { lon_0 }), "eck4" | "eckert4" => Arc::new(Eckert4 { lon_0 }), @@ -786,6 +788,26 @@ impl MapProjectionTrait for Robinson { } } +#[derive(Debug, Clone)] +pub struct EqualEarth { + pub lon_0: f64, +} + +impl MapProjectionTrait for EqualEarth { + fn proj_code(&self) -> &'static str { + "eqearth" + } + fn display_name(&self) -> &'static str { + "Equal Earth" + } + fn origin(&self) -> (f64, f64) { + (self.lon_0, 0.0) + } + fn to_proj_str(&self) -> String { + format!("+proj=eqearth +lon_0={}", self.lon_0) + } +} + #[derive(Debug, Clone)] pub struct Mollweide { pub lon_0: f64, @@ -1448,6 +1470,29 @@ mod tests { assert!(proj.slit_wkt(0.005).is_none()); } + #[test] + fn equal_earth_from_name_and_proj_str() { + let mut props = Parameters::new(); + props.insert("origin".to_string(), ParameterValue::Number(11.0)); + let proj = build_map_projection_trait(Some("equal_earth"), &props).unwrap(); + assert_eq!(proj.proj_code(), "eqearth"); + assert_eq!(proj.origin(), (11.0, 0.0)); + assert_eq!(proj.to_proj_str(), "+proj=eqearth +lon_0=11"); + + let proj = build_from_proj_str("+proj=eqearth +lon_0=11"); + assert_eq!(proj.proj_code(), "eqearth"); + assert_eq!(proj.to_proj_str(), "+proj=eqearth +lon_0=11"); + } + + #[test] + fn equal_earth_covers_whole_globe() { + let proj = build_from_proj_str("+proj=eqearth"); + assert_eq!(proj.lat_bounds(), (-90.0, 90.0)); + let wkt = proj.visible_area_wkt().unwrap(); + assert!(wkt.starts_with("POLYGON(("), "{wkt}"); + assert!(proj.slit_wkt(0.005).is_none()); + } + #[test] fn visible_area_gnomonic() { let proj = build_from_proj_str("+proj=gnom +lat_0=90 +lon_0=0");