Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.lock
target
1 change: 1 addition & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tab_spaces = 2
77 changes: 77 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,80 @@ repository = "https://github.com/cds-astro/cds-mapproj-rust/"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove that ? We prefer to not have it for the moment because we will take some time to know more about linting rules that we want (or not).


[lints]
rust.keyword_idents_2024 = "deny"
rust.non_ascii_idents = "deny"
rust.non_local_definitions = "deny"
rust.unsafe_op_in_unsafe_fn = "deny"
rust.missing_debug_implementations = "deny"
rust.missing_docs = "deny"
rust.dead_code = "deny"
rust.bad_style = "deny"
rust.improper_ctypes = "deny"
rust.while_true = "deny"
rust.patterns_in_fns_without_body = "deny"
rust.path_statements = "deny"
rust.overflowing_literals = "deny"
rust.no_mangle_generic_items = "deny"
rust.non_shorthand_field_patterns = "deny"
rust.unused_extern_crates = "deny"
rust.unused_import_braces = "deny"
rust.unused_qualifications = "deny"
rust.unused_results = "deny"
rust.unnameable_types = "deny"
rust.unreachable_pub = "deny"
rust.unused_lifetimes = "deny"
rust.unused_macro_rules = "deny"
rust.unused = "deny"
rust.unconditional_recursion = "deny"
rust.trivial_numeric_casts = "deny"
rust.trivial_casts = "deny"
rust.elided_lifetimes_in_paths = "deny"
rust.unexpected_cfgs = "deny"

clippy.perf = { level = "deny", priority = 1 }
clippy.pedantic = { level = "deny", priority = 1 }

# Pedantic overrides
clippy.float_cmp = {level = "allow", priority = 2 }
clippy.inline_always = {level = "allow", priority = 2 }
clippy.unreadable-literal = {level = "allow", priority = 2}
clippy.similar_names = {level = "allow", priority = 2}
clippy.too_many_lines = {level = "allow", priority = 2}
clippy.cast_precision_loss = {level = "allow", priority = 2}
clippy.cast_sign_loss = {level = "allow", priority = 2}
clippy.cast_possible_truncation = {level = "allow", priority = 2}
clippy.many_single_char_names = {level = "allow", priority = 2}
clippy.doc_markdown = {level = "allow", priority = 2}

# General list
clippy.too_many_arguments = "allow"
clippy.type_complexity = "allow"
clippy.allow_attributes_without_reason = "warn"
clippy.collection_is_never_read = "warn"
clippy.dbg_macro = "warn"
clippy.debug_assert_with_mut_call = "warn"
clippy.fn_to_numeric_cast_any = "warn"
clippy.infinite_loop = "warn"
clippy.large_stack_arrays = "warn"
clippy.mismatching_type_param_order = "warn"
clippy.missing_assert_message = "warn"
clippy.missing_fields_in_debug = "warn"
clippy.same_functions_in_if_condition = "warn"
clippy.semicolon_if_nothing_returned = "warn"
clippy.should_panic_without_expect = "warn"
clippy.todo = "warn"
clippy.unseparated_literal_suffix = "warn"
clippy.use_self = "warn"
clippy.cargo_common_metadata = "warn"
clippy.negative_feature_names = "warn"
clippy.redundant_feature_names = "warn"
clippy.wildcard_enum_match_arm = "warn"
clippy.wildcard_dependencies = "warn"
clippy.fallible_impl_from = "warn"
clippy.unneeded_field_pattern = "warn"
clippy.fn_params_excessive_bools = "warn"

clippy.must_use_candidate = "deny"
40 changes: 22 additions & 18 deletions src/conic/cod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Conic Equidistant projection.

use std::f64::consts::PI;

use crate::{CustomFloat, CanonicalProjection, ProjXY, XYZ, math::HALF_PI, conic::Conic, ProjBounds};
use std::f64::consts::{FRAC_PI_2, PI};

use crate::{conic::Conic, CanonicalProjection, CustomFloat, ProjBounds, ProjXY, XYZ};

/// Conic Equidistant projection.
#[derive(Debug, Clone)]
Expand All @@ -23,28 +23,33 @@ impl Default for Cod {
}

impl Cod {

// default theta1 = theta2 = 45 deg
/// Default theta1 = theta2 = 45 deg
#[must_use]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remove all these must_use decorator ? In many cases I would not say not using the result would automatically lead to a bug. Also, lots of CI define warnings as errors (RUSTFLAGS="-D warnings" cargo build) and this may break the CI whereas no particular bug is really there (e.g. a call that is not necessary that is why its result is not used).

pub fn new() -> Self {
Self::from_params(HALF_PI.half(), 0.0)
Self::from_params(FRAC_PI_2.half(), 0.0)
}

#[must_use]
/// Construct from provided `theta_a` and `nu` parameters.
pub fn from_params(theta_a: f64, nu: f64) -> Self {
let conic = Conic::from_params(theta_a, nu);
let sin_ta = conic.ta.sin();
let cot_ta = 1.0 / conic.ta.tan();
let (c, y0) = if conic.nu == 0.0 {
debug_assert_eq!(conic.theta1, conic.theta2);
debug_assert_eq!(conic.theta1, conic.theta2, "Angles must match");
(sin_ta, cot_ta)
} else {
let sin_nu = conic.nu.sin();
((sin_ta * sin_nu) / conic.nu, conic.nu * cot_ta / conic.nu.tan())
(
(sin_ta * sin_nu) / conic.nu,
conic.nu * cot_ta / conic.nu.tan(),
)
};
let ta_plus_y0 = conic.ta + y0;
let (r_min, r_max) = if ta_plus_y0 >= 0.0 {
(ta_plus_y0 - HALF_PI, ta_plus_y0 + HALF_PI)
(ta_plus_y0 - FRAC_PI_2, ta_plus_y0 + FRAC_PI_2)
} else {
(ta_plus_y0 + HALF_PI, ta_plus_y0 - HALF_PI)
(ta_plus_y0 + FRAC_PI_2, ta_plus_y0 - FRAC_PI_2)
};
let yrange = if conic.negative_ta {
Some(y0 - r_max * (PI * c).cos().abs()..=y0 + r_max)
Expand All @@ -58,17 +63,12 @@ impl Cod {
r2_min: r_min.pow2(),
r2_max: r_max.pow2(),
ta_plus_y0,
proj_bounds: ProjBounds::new(
Some(-r_max..=r_max),
yrange
)
proj_bounds: ProjBounds::new(Some(-r_max..=r_max), yrange),
}
}
}


impl CanonicalProjection for Cod {

const NAME: &'static str = "Conic Equidistant";
const WCS_NAME: &'static str = "COD";

Expand All @@ -92,8 +92,12 @@ impl CanonicalProjection for Cod {
let y2d = self.y0 - pos.y;
let r2 = x2d.pow2() + y2d.pow2();
if (self.r2_min..=self.r2_max).contains(&r2) {
let r = if self.conic.negative_ta { -(r2.sqrt()) } else { r2.sqrt() };
let lon = (x2d / r).atan2(y2d / r) / self.c; // / r important because of its sign
let r = if self.conic.negative_ta {
-(r2.sqrt())
} else {
r2.sqrt()
};
let lon = (x2d / r).atan2(y2d / r) / self.c; // / r important because of its sign
if (-PI - EPS..PI + EPS).contains(&lon) {
let (sinb, cosb) = (self.ta_plus_y0 - r).sin_cos();
let (sinl, cosl) = lon.sin_cos();
Expand Down
44 changes: 25 additions & 19 deletions src/conic/coe.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Conic Equal Area projection.

use std::f64::consts::PI;
use std::f64::consts::{FRAC_PI_2, PI};

use crate::{CustomFloat, CanonicalProjection, ProjXY, XYZ, conic::Conic, ProjBounds};
use crate::math::HALF_PI;
use crate::{conic::Conic, CanonicalProjection, CustomFloat, ProjBounds, ProjXY, XYZ};

/// Conic Equal Area projection.
#[derive(Debug, Clone)]
pub struct Coe {
conic: Conic,
one_plus_sint1_sint2: f64,
Expand All @@ -25,12 +25,14 @@ impl Default for Coe {
}

impl Coe {

// default theta1 = theta2 = 45 deg
/// Construct default theta1 = theta2 = 45 deg
#[must_use]
pub fn new() -> Self {
Self::from_params(HALF_PI.half(), 0.0)
Self::from_params(FRAC_PI_2.half(), 0.0)
}

#[must_use]
/// Construct from provided `theta_a` and `nu` parameters.
pub fn from_params(theta_a: f64, nu: f64) -> Self {
let conic = Conic::from_params(theta_a, nu);
let sin_t1 = conic.theta1.sin();
Expand All @@ -40,16 +42,16 @@ impl Coe {
let gamma = sin_t1 + sin_t2;
let c = gamma.half();
let c2 = c.pow2();
let y0 = (one_plus_sint1_sint2 - gamma * sin_ta).sqrt() / c;
let y0 = (one_plus_sint1_sint2 - gamma * sin_ta).sqrt() / c;
let (r2_min, r2_max) = if gamma >= 0.0 {
(
(one_plus_sint1_sint2 - gamma) / c2,
(one_plus_sint1_sint2 + gamma) / c2
(one_plus_sint1_sint2 + gamma) / c2,
)
} else {
(
(one_plus_sint1_sint2 + gamma) / c2,
(one_plus_sint1_sint2 - gamma) / c2
(one_plus_sint1_sint2 - gamma) / c2,
)
};
debug_assert!(r2_min <= r2_max);
Expand All @@ -61,19 +63,19 @@ impl Coe {
};
Self {
conic,
one_plus_sint1_sint2, gamma, c, c2, y0,
r2_min, r2_max,
proj_bounds: ProjBounds::new(
Some(-r_max..=r_max),
yrange
)
one_plus_sint1_sint2,
gamma,
c,
c2,
y0,
r2_min,
r2_max,
proj_bounds: ProjBounds::new(Some(-r_max..=r_max), yrange),
}
}
}


impl CanonicalProjection for Coe {

const NAME: &'static str = "Conic Equal Area";
const WCS_NAME: &'static str = "COE";

Expand All @@ -99,8 +101,12 @@ impl CanonicalProjection for Coe {
let y2d = self.y0 - pos.y;
let r2 = x2d.pow2() + y2d.pow2();
if (self.r2_min..=self.r2_max).contains(&r2) {
let r = if self.conic.negative_ta { -(r2.sqrt()) } else { r2.sqrt() };
let lon = (x2d / r).atan2(y2d / r) / self.c; // / r important because of its sign
let r = if self.conic.negative_ta {
-(r2.sqrt())
} else {
r2.sqrt()
};
let lon = (x2d / r).atan2(y2d / r) / self.c; // / r important because of its sign
if (-PI - EPS..PI + EPS).contains(&lon) {
let z = (self.one_plus_sint1_sint2 - self.c2 * r2) / self.gamma;
if (-1.0..1.0).contains(&z) {
Expand Down
42 changes: 22 additions & 20 deletions src/conic/coo.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
//! Conic Orthomorphic projection.

use std::f64::consts::PI;
use std::f64::consts::{FRAC_PI_2, PI};

use crate::{CustomFloat, CanonicalProjection, ProjXY, XYZ, math::HALF_PI, conic::Conic, ProjBounds};
use crate::{conic::Conic, CanonicalProjection, CustomFloat, ProjBounds, ProjXY, XYZ};

/// Conic Orthomorphic projection.
#[derive(Debug, Clone, Copy)]
pub struct Coo {
conic: Conic,
c: f64,
Expand All @@ -20,53 +21,50 @@ impl Default for Coo {
}

impl Coo {

// default theta1 = theta2 = 45 deg
/// Construct with default theta1 = theta2 = 45 deg
#[must_use]
pub fn new() -> Self {
Self::from_params(HALF_PI.half(), 0.0)
Self::from_params(FRAC_PI_2.half(), 0.0)
}

/// Construct from provided `theta_a` and `nu` parameters.
#[must_use]
pub fn from_params(theta_a: f64, nu: f64) -> Self {
let conic = Conic::from_params(theta_a, nu);
let cos_t1 = conic.theta1.cos();
let tan_ft1 = (HALF_PI - conic.theta1).half().tan();
let tan_ft1 = (FRAC_PI_2 - conic.theta1).half().tan();
let c = if conic.nu == 0.0 {
debug_assert_eq!(conic.theta1, conic.theta2);
conic.theta1.sin()
} else {
(conic.theta2.cos() / cos_t1).ln() / ((HALF_PI - conic.theta2).half().tan() / tan_ft1).ln()
(conic.theta2.cos() / cos_t1).ln() / ((FRAC_PI_2 - conic.theta2).half().tan() / tan_ft1).ln()
};
let psi = cos_t1 / (c * tan_ft1.powf(c));
let y0 = psi * (HALF_PI - conic.ta).half().tan().powf(c);
let y0 = psi * (FRAC_PI_2 - conic.ta).half().tan().powf(c);
let one_over_c = 1.0 / c;
Self {
conic,
c,
one_over_c,
y0,
psi
psi,
}
}
}


impl CanonicalProjection for Coo {

const NAME: &'static str = "Conic Orthomorphic";
const WCS_NAME: &'static str = "COO";

fn bounds(&self) -> &ProjBounds {
const PROJ_BOUNDS: ProjBounds = ProjBounds::new(
None,
None
);
const PROJ_BOUNDS: ProjBounds = ProjBounds::new(None, None);
&PROJ_BOUNDS
}

fn proj(&self, xyz: &XYZ) -> Option<ProjXY> {
let lon = xyz.y.atan2(xyz.x);
// Use something else than asin?
let r = self.psi * (HALF_PI - xyz.z.asin()).half().tan().powf(self.c);
let r = self.psi * (FRAC_PI_2 - xyz.z.asin()).half().tan().powf(self.c);
let (sinc, cosc) = (self.c * lon).sin_cos();
Some(ProjXY::new(r * sinc, self.y0 - r * cosc))
}
Expand All @@ -76,10 +74,14 @@ impl CanonicalProjection for Coo {
let x2d = pos.x;
let y2d = self.y0 - pos.y;
let r2 = x2d.pow2() + y2d.pow2();
let r = if self.conic.negative_ta { -(r2.sqrt()) } else { r2.sqrt() };
let lon = (x2d / r).atan2(y2d / r) / self.c; // / r important because of its sign
let r = if self.conic.negative_ta {
-(r2.sqrt())
} else {
r2.sqrt()
};
let lon = (x2d / r).atan2(y2d / r) / self.c; // / r important because of its sign
if (-PI - EPS..PI + EPS).contains(&lon) {
let lat = HALF_PI - (r / self.psi).powf(self.one_over_c).atan().twice();
let lat = FRAC_PI_2 - (r / self.psi).powf(self.one_over_c).atan().twice();
let (sinb, cosb) = lat.sin_cos();
let (sinl, cosl) = lon.sin_cos();
Some(XYZ::new(cosb * cosl, cosb * sinl, sinb))
Expand Down
Loading