diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2def93a..26f02de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,9 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + submodules: recursive + fetch-depth: 0 - name: Set up Bun uses: oven-sh/setup-bun@v1 @@ -29,27 +32,27 @@ jobs: - name: Set up Rust uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: nightly override: true - name: Install Rustfmt - run: rustup component add rustfmt + run: rustup component add rustfmt --toolchain nightly - name: Install Clippy - run: rustup component add clippy + run: rustup component add clippy --toolchain nightly - name: Run Clippy - run: cargo clippy -- -D warnings + run: cargo clippy --workspace --package geometry -- -D warnings shell: bash - name: Build Rust project - run: cargo build + run: cargo +nightly build shell: bash - name: Check Formatting - run: cargo fmt -- --check + run: cargo +nightly fmt -- --check shell: bash - name: Run Rust tests - run: cargo test --lib + run: cargo +nightly test --lib shell: bash diff --git a/Cargo.lock b/Cargo.lock index f145ddf..9bf1629 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - [[package]] name = "geometry" version = "0.1.0" @@ -29,25 +23,6 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "lol_alloc" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e5106554cabc97552dcadf54f57560ae6af3276652f82ca2be06120dc4c5dc" -dependencies = [ - "spin", -] - [[package]] name = "memchr" version = "2.7.4" @@ -78,20 +53,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "s2cell" -version = "0.1.0" -dependencies = [ - "geometry", - "lol_alloc", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "serde" version = "1.0.210" @@ -124,15 +85,6 @@ dependencies = [ "serde", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "syn" version = "2.0.79" @@ -144,13 +96,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "uint64" -version = "0.1.0" -dependencies = [ - "lol_alloc", -] - [[package]] name = "unicode-ident" version = "1.0.13" diff --git a/Cargo.toml b/Cargo.toml index a98d531..98dc7d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,8 +2,8 @@ resolver = "2" members = [ "rust/geometry", - "rust/s2cell", - "rust/uint64" + # "rust/s2cell", + # "rust/uint64" ] default-members = [ @@ -15,7 +15,7 @@ name = "s2-tools" version = "1.0.0" edition = "2021" authors = ["Craig O'Connor "] -description = "Triangle mesh designed to be fast, efficient, and sphere capable." +description = "A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2." documentation = "https://docs.rs/s2-tools" homepage = "https://github.com/OpenS2/s2-tools" repository = "https://github.com/OpenS2/s2-tools" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 8385f79..c786de6 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "stable" +channel = "nightly" components = ["rustc", "rustfmt", "clippy", "rust-docs", "rust-analyzer", "miri"] targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown", "wasm32-wasi", "aarch64-apple-darwin"] profile = "complete" diff --git a/rust/geometry/cargo.toml b/rust/geometry/cargo.toml index a093459..0710151 100644 --- a/rust/geometry/cargo.toml +++ b/rust/geometry/cargo.toml @@ -12,6 +12,7 @@ keywords = ["open", "vector", "tile", "gis", "low-cost-code"] default = ["quadratic"] # Default feature quadratic = [] # Feature for quadratic projection tan = [] # Feature for tangential projection +linear = [] # Feature for linear projection [dependencies] libm = "0.2" diff --git a/rust/geometry/src/geometry.rs b/rust/geometry/src/geometry.rs index 1edbd0a..a52c850 100644 --- a/rust/geometry/src/geometry.rs +++ b/rust/geometry/src/geometry.rs @@ -925,7 +925,11 @@ mod tests { } ); let point_str = serde_json::to_string(&point).unwrap(); - assert_eq!(point_str, "{\"type\":\"Point3D\",\"coordinates\":[0.0,0.0,0.0],\"bbox\":[0.0,0.0,1.0,1.0,0.0,1.0]}"); + assert_eq!( + point_str, + "{\"type\":\"Point3D\",\"coordinates\":[0.0,0.0,0.0],\"bbox\":[0.0,0.0,1.0,1.0,0.0,1.\ + 0]}" + ); let str_point: Point3DGeometry = serde_json::from_str(&point_str).unwrap(); assert_eq!(str_point, point); } @@ -1075,7 +1079,11 @@ mod tests { } ); let polygon_str = serde_json::to_string(&polygon).unwrap(); - assert_eq!(polygon_str, "{\"type\":\"Polygon3D\",\"coordinates\":[[[0.0,0.0,0.0],[1.0,1.0,1.0],[0.0,1.0,1.0]]]}"); + assert_eq!( + polygon_str, + "{\"type\":\"Polygon3D\",\"coordinates\":[[[0.0,0.0,0.0],[1.0,1.0,1.0],[0.0,1.0,1.\ + 0]]]}" + ); let str_polygon: Polygon3DGeometry = serde_json::from_str(&polygon_str).unwrap(); assert_eq!(str_polygon, polygon); } @@ -1127,7 +1135,8 @@ mod tests { let multi_polygon_str = serde_json::to_string(&multi_polygon).unwrap(); assert_eq!( multi_polygon_str, - "{\"type\":\"MultiPolygon3D\",\"coordinates\":[[[[0.0,0.0,0.0],[1.0,1.0,1.0],[0.0,1.0,1.0]]]]}" + "{\"type\":\"MultiPolygon3D\",\"coordinates\":[[[[0.0,0.0,0.0],[1.0,1.0,1.0],[0.0,1.0,\ + 1.0]]]]}" ); let str_multi_polygon: MultiPolygon3DGeometry = serde_json::from_str(&multi_polygon_str).unwrap(); diff --git a/rust/geometry/src/lib.rs b/rust/geometry/src/lib.rs index 040e22e..7dc2929 100644 --- a/rust/geometry/src/lib.rs +++ b/rust/geometry/src/lib.rs @@ -193,13 +193,7 @@ impl Feature { geometry: Geometry, metadata: Option, ) -> Self { - Self { - _type: "Feature".to_string(), - id, - properties, - geometry, - metadata, - } + Self { _type: "Feature".to_string(), id, properties, geometry, metadata } } } @@ -248,14 +242,7 @@ impl VectorFeature { geometry: VectorGeometry, metadata: Option, ) -> Self { - Self { - _type: "S2Feature".to_string(), - face, - id, - properties, - geometry, - metadata, - } + Self { _type: "S2Feature".to_string(), face, id, properties, geometry, metadata } } /// Create a new VectorFeature using an input VectorFeature. Assign new geometry if provided diff --git a/rust/geometry/src/lonlat.rs b/rust/geometry/src/lonlat.rs index c46092c..53b13be 100644 --- a/rust/geometry/src/lonlat.rs +++ b/rust/geometry/src/lonlat.rs @@ -1,10 +1,10 @@ // const S1Angle = @import("s1/angle.zig").S1Angle; // const S2Point = @import("point.zig").S2Point; -use libm::{fabs, cos, sin, atan2, sqrt, asin}; -use core::f64::consts::PI; -use crate::s2::point::S2Point; use crate::s1::angle::S1Angle; +use crate::s2::point::S2Point; +use core::f64::consts::PI; +use libm::{asin, atan2, cos, fabs, sin, sqrt}; /// This class represents a point on the unit sphere as a pair /// of latitude-longitude coordinates. Like the rest of the "geometry" @@ -22,7 +22,7 @@ impl LonLat { /// The default constructor sets the latitude and longitude to zero. This is /// mainly useful when declaring arrays, STL containers, etc. pub fn new(lon: f64, lat: f64) -> Self { - LonLat{ lon, lat } + LonLat { lon, lat } } /// Constructor. The latitude and longitude are allowed to be outside @@ -35,7 +35,9 @@ impl LonLat { // Convert a direction vector (not necessarily unit length) to an LonLat. pub fn from_s2_point(p: &S2Point) -> LonLat { let ll = LonLat::new(LonLat::latitude(p).radians, LonLat::longitude(p).radians); - if !ll.is_valid() { unreachable!(); } + if !ll.is_valid() { + unreachable!(); + } ll } @@ -95,7 +97,11 @@ impl LonLat { self.lon_angle().degrees() } pub fn from_axis(&self, axis: u8) -> f64 { - if axis == 0 { self.lon } else { self.lat } + if axis == 0 { + self.lon + } else { + self.lat + } } /// Return the latitude or longitude coordinates in degrees. @@ -106,8 +112,7 @@ impl LonLat { /// Return true if the latitude is between -90 and 90 degrees inclusive /// and the longitude is between -180 and 180 degrees inclusive. pub fn is_valid(&self) -> bool { - fabs(self.lat_degrees()) <= (PI / 2.0) && - fabs(self.lon_degrees()) <= PI + fabs(self.lat_degrees()) <= (PI / 2.0) && fabs(self.lon_degrees()) <= PI } // // Clamps the latitude to the range [-90, 90] degrees, and adds or subtracts @@ -129,7 +134,9 @@ impl LonLat { // cap.AddPoint(S2Point(latlon)); pub fn to_point(&self) -> S2Point { // TODO: - if !self.is_valid() { unreachable!(); } + if !self.is_valid() { + unreachable!(); + } // S2_DLOG_IF(ERROR, !is_valid()) // << "Invalid LonLat in LonLat::ToPoint: " << *this; let phi: f64 = self.lat; @@ -157,7 +164,9 @@ impl LonLat { // you might as well just convert both arguments to S2Points and compute the // distance that way (which gives about 15 digits of accuracy for all // distances). - if !self.is_valid() || !b.is_valid() { unreachable!(); } + if !self.is_valid() || !b.is_valid() { + unreachable!(); + } let lat1 = self.lat; let lat2 = b.lat; diff --git a/rust/geometry/src/planets/mod.rs b/rust/geometry/src/planets/mod.rs index 2d71a35..4f90a6c 100644 --- a/rust/geometry/src/planets/mod.rs +++ b/rust/geometry/src/planets/mod.rs @@ -1 +1 @@ -pub mod earth; \ No newline at end of file +pub mod earth; diff --git a/rust/geometry/src/s1/angle.rs b/rust/geometry/src/s1/angle.rs index 12fec6c..9a9352e 100644 --- a/rust/geometry/src/s1/angle.rs +++ b/rust/geometry/src/s1/angle.rs @@ -53,19 +53,19 @@ use core::f64::consts::PI; /// the default copy constructor and assignment operator. #[derive(Copy, Clone, Default, PartialEq, Debug)] pub struct S1Angle { - pub radians: f64 + pub radians: f64, } impl S1Angle { pub fn new(radians: f64) -> Self { - Self{ radians } + Self { radians } } pub fn from_degrees(degrees: f64) -> Self { - Self{ radians: degrees * (PI / 180.0) } + Self { radians: degrees * (PI / 180.0) } } pub fn infinity() -> Self { - Self{ radians: f64::INFINITY } + Self { radians: f64::INFINITY } } pub fn degrees(&self) -> f64 { diff --git a/rust/geometry/src/s1/mod.rs b/rust/geometry/src/s1/mod.rs index 578dafc..8f932ea 100644 --- a/rust/geometry/src/s1/mod.rs +++ b/rust/geometry/src/s1/mod.rs @@ -1 +1 @@ -pub mod angle; \ No newline at end of file +pub mod angle; diff --git a/rust/geometry/src/s2/cellid.rs b/rust/geometry/src/s2/cellid.rs index 31f97cc..231ff97 100644 --- a/rust/geometry/src/s2/cellid.rs +++ b/rust/geometry/src/s2/cellid.rs @@ -650,28 +650,17 @@ impl S2CellId { let k_limit = 1. + 2.220_446_049_250_313e-16; let u = fmax( -k_limit, - fmin( - k_limit, - k_scale * (2. * (i as f64 - (K_MAX_SIZE as f64) / 2.) + 1.), - ), + fmin(k_limit, k_scale * (2. * (i as f64 - (K_MAX_SIZE as f64) / 2.) + 1.)), ); let v = fmax( -k_limit, - fmin( - k_limit, - k_scale * (2. * (j as f64 - (K_MAX_SIZE as f64) / 2.) + 1.), - ), + fmin(k_limit, k_scale * (2. * (j as f64 - (K_MAX_SIZE as f64) / 2.) + 1.)), ); // Find the leaf cell coordinates on the adjacent face, and convert // them to a cell id at the appropriate level. let (n_face, nu, nv) = xyz_to_face_uv(&face_uv_to_xyz(face, u, v)); - S2CellId::from_face_ij( - n_face, - st_to_ij(0.5 * (nu + 1.)), - st_to_ij(0.5 * (nv + 1.)), - None, - ) + S2CellId::from_face_ij(n_face, st_to_ij(0.5 * (nu + 1.)), st_to_ij(0.5 * (nv + 1.)), None) } /// Given an S2CellID, find it's nearest neighbors associated with it diff --git a/rust/geometry/src/s2/coords.rs b/rust/geometry/src/s2/coords.rs index b27946d..88977a8 100644 --- a/rust/geometry/src/s2/coords.rs +++ b/rust/geometry/src/s2/coords.rs @@ -195,10 +195,10 @@ pub fn st_to_uvtan(s_: f64) -> f64 { #[cfg(feature = "quadratic")] pub const ST_TO_UV: fn(f64) -> f64 = st_to_uvquadratic; /// If settings are updated you can use the tangent projection -#[cfg(all(not(feature = "quadratic"), feature = "tan"))] +#[cfg(feature = "tan")] pub const ST_TO_UV: fn(f64) -> f64 = st_to_uvtan; /// If settings are updated you can use the linear projection -#[cfg(all(not(feature = "quadratic"), not(feature = "tan")))] +#[cfg(feature = "linear")] pub const ST_TO_UV: fn(f64) -> f64 = st_to_uvlinear; /// The inverse of the STtoUV transformation. Note that it is not always @@ -226,10 +226,10 @@ pub fn uv_to_st_tan(u: f64) -> f64 { #[cfg(feature = "quadratic")] pub const UV_TO_ST: fn(f64) -> f64 = uv_to_st_quadratic; /// If settings are updated you can use the tangent projection -#[cfg(all(not(feature = "quadratic"), feature = "tan"))] +#[cfg(feature = "tan")] pub const UV_TO_ST: fn(f64) -> f64 = uv_to_st_tan; /// If settings are updated you can use the linear projection -#[cfg(all(not(feature = "quadratic"), not(feature = "tan")))] +#[cfg(feature = "linear")] pub const UV_TO_ST: fn(f64) -> f64 = uv_to_stlinear; /// Convert the i- or j-index of a leaf cell to the minimum corresponding s- diff --git a/rust/geometry/src/tile.rs b/rust/geometry/src/tile.rs index 0ee7059..32624d9 100644 --- a/rust/geometry/src/tile.rs +++ b/rust/geometry/src/tile.rs @@ -34,11 +34,7 @@ pub struct Tile { impl Tile { /// Create a new Tile pub fn new(id: CellId) -> Self { - Self { - id, - layers: BTreeMap::new(), - transformed: false, - } + Self { id, layers: BTreeMap::new(), transformed: false } } /// Returns true if the tile is empty of features @@ -62,14 +58,9 @@ impl Tile { .unwrap_or_else(|| "default".to_string()); // Fall back to "default" if none found if !self.layers.contains_key(&layer_name) { - self.layers - .insert(layer_name.clone(), Layer::new(layer_name.clone())); + self.layers.insert(layer_name.clone(), Layer::new(layer_name.clone())); } - self.layers - .get_mut(&layer_name) - .unwrap() - .features - .push(feature); + self.layers.get_mut(&layer_name).unwrap().features.push(feature); } /// Simplify the geometry to have a tolerance which will be relative to the tile's zoom level. @@ -102,10 +93,7 @@ pub struct Layer { impl Layer { /// Create a new Layer pub fn new(name: String) -> Self { - Self { - name, - features: vec![], - } + Self { name, features: vec![] } } } @@ -178,9 +166,7 @@ impl TileStore { Some(tile_store.maxzoom), None, ); - features - .into_iter() - .for_each(|feature| tile_store.add_feature(feature)); + features.into_iter().for_each(|feature| tile_store.add_feature(feature)); for i in 0..6 { tile_store.split_tile(CellId::from_face(i), None, None); } @@ -191,27 +177,17 @@ impl TileStore { /// Add a feature to the tile store pub fn add_feature(&mut self, feature: VectorFeature) { let face: u8 = feature.face.into(); - let tile = self - .tiles - .entry(CellId::from_face(face)) - .or_insert_with(|| { - self.faces.insert(feature.face); - Tile::new(CellId::from_face(face)) - }); + let tile = self.tiles.entry(CellId::from_face(face)).or_insert_with(|| { + self.faces.insert(feature.face); + Tile::new(CellId::from_face(face)) + }); tile.add_feature(feature, None); } /// Split tiles given a range fn split_tile(&mut self, start_id: CellId, end_id: Option, end_zoom: Option) { - let TileStore { - buffer, - tiles, - tolerance, - maxzoom, - index_maxzoom, - .. - } = self; + let TileStore { buffer, tiles, tolerance, maxzoom, index_maxzoom, .. } = self; let end_zoom = end_zoom.unwrap_or(*maxzoom); let mut stack: Vec = vec![start_id]; // avoid recursion by using a processing queue @@ -288,17 +264,15 @@ impl VectorGeometry { let zoom = (1 << (zoom as u64)) as f64; match self { VectorGeometry::Point(p) => p.coordinates.transform(zoom, ti, tj), - VectorGeometry::LineString(l) | VectorGeometry::MultiPoint(l) => l - .coordinates - .iter_mut() - .for_each(|p| p.transform(zoom, ti, tj)), + VectorGeometry::LineString(l) | VectorGeometry::MultiPoint(l) => { + l.coordinates.iter_mut().for_each(|p| p.transform(zoom, ti, tj)) + } VectorGeometry::MultiLineString(l) | VectorGeometry::Polygon(l) => l .coordinates .iter_mut() .for_each(|l| l.iter_mut().for_each(|p| p.transform(zoom, ti, tj))), VectorGeometry::MultiPolygon(l) => l.coordinates.iter_mut().for_each(|p| { - p.iter_mut() - .for_each(|l| l.iter_mut().for_each(|p| p.transform(zoom, ti, tj))) + p.iter_mut().for_each(|l| l.iter_mut().for_each(|p| p.transform(zoom, ti, tj))) }), } } diff --git a/rustfmt.toml b/rustfmt.toml index b42bd16..dc45647 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,13 @@ max_width = 100 +use_small_heuristics = "Max" +single_line_if_else_max_width = 100 +single_line_let_else_max_width = 100 +# Indentation settings tab_spaces = 4 -use_small_heuristics = "Default" format_strings = true +# Prevents rustfmt from forcing multi-line formatting for match arms, if-let expressions, etc. +struct_lit_single_line = true +# Indentation settings to control how nested blocks are handled +indent_style = "Block" +# Controls how rustfmt treats single-line blocks +empty_item_single_line = true