From 0b873d7164abeb898d27fff3fbd1b68329adc96b Mon Sep 17 00:00:00 2001 From: Derick M <58572875+TurtIeSocks@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:56:49 -0400 Subject: [PATCH] refactor: more consistent and flexible precision trait usage --- server/Cargo.lock | 6 ++--- server/Cargo.toml | 2 +- server/api/Cargo.toml | 2 +- server/api/src/public/v1/convert.rs | 7 ++--- server/model/Cargo.toml | 2 +- server/model/src/api/collection.rs | 8 ++++-- server/model/src/api/geometry.rs | 38 +++++++++++++++++----------- server/model/src/api/mod.rs | 1 - server/model/src/api/point_array.rs | 2 +- server/model/src/api/point_struct.rs | 2 +- server/model/src/api/single_vec.rs | 4 ++- server/model/src/db/geofence.rs | 5 ++-- server/model/src/utils/mod.rs | 18 +++++++++++-- 13 files changed, 62 insertions(+), 35 deletions(-) diff --git a/server/Cargo.lock b/server/Cargo.lock index c9a1ef1c..0bccb69a 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -431,7 +431,7 @@ checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "api" -version = "1.5.0" +version = "1.5.1" dependencies = [ "actix-files", "actix-session", @@ -2084,7 +2084,7 @@ dependencies = [ [[package]] name = "koji" -version = "1.4.2" +version = "1.5.1" dependencies = [ "api", "dotenv", @@ -2312,7 +2312,7 @@ dependencies = [ [[package]] name = "model" -version = "1.5.0" +version = "1.5.1" dependencies = [ "chrono", "futures", diff --git a/server/Cargo.toml b/server/Cargo.toml index 89e8fd57..e91b9e25 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "koji" -version = "1.4.2" +version = "1.5.1" edition = "2021" [workspace] diff --git a/server/api/Cargo.toml b/server/api/Cargo.toml index 36f9a185..87b064fc 100644 --- a/server/api/Cargo.toml +++ b/server/api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "api" -version = "1.5.0" +version = "1.5.1" edition = "2021" publish = false diff --git a/server/api/src/public/v1/convert.rs b/server/api/src/public/v1/convert.rs index 7a13b6dd..8119351e 100644 --- a/server/api/src/public/v1/convert.rs +++ b/server/api/src/public/v1/convert.rs @@ -8,6 +8,7 @@ use model::{ FeatureHelpers, GeometryHelpers, ToCollection, ToFeature, }, db::sea_orm_active_enums::Type, + utils::TrimPrecision, }; #[post("/data")] @@ -21,11 +22,11 @@ async fn convert_data(payload: web::Json) -> Result { .. } = payload.into_inner().init(Some("convert_data")); - let area = if arg_simplify { area.simplify() } else { area }; - let area = area + let area = if arg_simplify { area.simplify() } else { area } .into_iter() .map(|feat| feat.remove_internal_props()) - .collect(); + .collect::() + .trim_precision(6); Ok(utils::response::send( area, diff --git a/server/model/Cargo.toml b/server/model/Cargo.toml index efd989d7..78f21567 100644 --- a/server/model/Cargo.toml +++ b/server/model/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "model" -version = "1.5.0" +version = "1.5.1" edition = "2021" publish = false diff --git a/server/model/src/api/collection.rs b/server/model/src/api/collection.rs index b4d64e33..2c2c8265 100644 --- a/server/model/src/api/collection.rs +++ b/server/model/src/api/collection.rs @@ -1,3 +1,5 @@ +use utils::TrimPrecision; + use self::utils::sql_raw; use super::{args::UnknownId, multi_vec::MultiVec, *}; @@ -39,13 +41,15 @@ impl GeometryHelpers for FeatureCollection { }) .collect() } +} - fn to_f32(self) -> Self { +impl TrimPrecision for FeatureCollection { + fn trim_precision(self, precision: u32) -> Self { self.into_iter() .map(|feat| { if let Some(geometry) = feat.geometry { Feature { - geometry: Some(geometry.to_f32()), + geometry: Some(geometry.trim_precision(precision)), ..feat } } else { diff --git a/server/model/src/api/geometry.rs b/server/model/src/api/geometry.rs index 67b59dde..fae4a14e 100644 --- a/server/model/src/api/geometry.rs +++ b/server/model/src/api/geometry.rs @@ -1,4 +1,5 @@ use geo::{MultiPolygon, Polygon, Simplify}; +use utils::TrimPrecision; use super::*; @@ -37,22 +38,24 @@ impl EnsurePoints for Geometry { } } -fn trim_precision(data: Vec>>) -> Vec>> { - let mut formatted_data = Vec::new(); +impl TrimPrecision for Vec>> { + fn trim_precision(self, precision: u32) -> Self { + let mut formatted_data = Vec::new(); - for outer_vec in data { - let mut formatted_outer_vec = Vec::new(); - for inner_vec in outer_vec { - let mut formatted_inner_vec = Vec::new(); - for num in inner_vec { - formatted_inner_vec.push(format!("{:.6}", num).parse().unwrap()); + for outer_vec in self { + let mut formatted_outer_vec = Vec::new(); + for inner_vec in outer_vec { + let mut formatted_inner_vec = Vec::new(); + for num in inner_vec { + formatted_inner_vec.push(num.trim_precision(precision)); + } + formatted_outer_vec.push(formatted_inner_vec); } - formatted_outer_vec.push(formatted_inner_vec); + formatted_data.push(formatted_outer_vec); } - formatted_data.push(formatted_outer_vec); - } - formatted_data + formatted_data + } } impl GeometryHelpers for Geometry { @@ -71,13 +74,18 @@ impl GeometryHelpers for Geometry { geometry.bbox = geometry.get_bbox(); geometry } - fn to_f32(self) -> Self { +} + +impl TrimPrecision for Geometry { + fn trim_precision(self, precision: u32) -> Self { let mut geometry = match self.value { - Value::Polygon(value) => Geometry::from(geojson::Value::Polygon(trim_precision(value))), + Value::Polygon(value) => { + Geometry::from(geojson::Value::Polygon(value.trim_precision(precision))) + } Value::MultiPolygon(value) => { let mut formatted_data: Vec>>> = Vec::new(); for outer_vec in value { - formatted_data.push(trim_precision(outer_vec)) + formatted_data.push(outer_vec.trim_precision(precision)) } Geometry::from(geojson::Value::MultiPolygon(formatted_data)) } diff --git a/server/model/src/api/mod.rs b/server/model/src/api/mod.rs index 5cbd4de4..c913ad46 100644 --- a/server/model/src/api/mod.rs +++ b/server/model/src/api/mod.rs @@ -48,7 +48,6 @@ pub trait ValueHelpers { pub trait GeometryHelpers { fn simplify(self) -> Self; - fn to_f32(self) -> Self; } pub trait FeatureHelpers { diff --git a/server/model/src/api/point_array.rs b/server/model/src/api/point_array.rs index 75141d5f..45f45519 100644 --- a/server/model/src/api/point_array.rs +++ b/server/model/src/api/point_array.rs @@ -76,7 +76,7 @@ impl ToCollection for PointArray { impl ToText for PointArray { fn to_text(self, sep_1: &str, sep_2: &str, _poly_sep: bool) -> String { - format!("{}{}{}{}", self[0] as f32, sep_1, self[1] as f32, sep_2) + format!("{}{}{}{}", self[0], sep_1, self[1], sep_2) } } diff --git a/server/model/src/api/point_struct.rs b/server/model/src/api/point_struct.rs index 9ab882ef..dac8ccbb 100644 --- a/server/model/src/api/point_struct.rs +++ b/server/model/src/api/point_struct.rs @@ -82,7 +82,7 @@ impl ToCollection for PointStruct { impl ToText for PointStruct { fn to_text(self, sep_1: &str, sep_2: &str, _poly_sep: bool) -> String { - format!("{}{}{}{}", self.lat as f32, sep_1, self.lon as f32, sep_2) + format!("{}{}{}{}", self.lat, sep_1, self.lon, sep_2) } } diff --git a/server/model/src/api/single_vec.rs b/server/model/src/api/single_vec.rs index 2694cf53..5caafe27 100644 --- a/server/model/src/api/single_vec.rs +++ b/server/model/src/api/single_vec.rs @@ -1,3 +1,5 @@ +use utils::TrimPrecision; + use super::{collection::Default, *}; pub type SingleVec = Vec; @@ -46,7 +48,7 @@ impl GetBbox for SingleVec { bbox[3] = point[0] } } - Some(bbox) + Some(bbox.into_iter().map(|e| e.trim_precision(6)).collect()) } } diff --git a/server/model/src/db/geofence.rs b/server/model/src/db/geofence.rs index e4dece6c..0671fb76 100644 --- a/server/model/src/db/geofence.rs +++ b/server/model/src/db/geofence.rs @@ -15,8 +15,6 @@ use crate::{ }, }; -use self::api::GeometryHelpers; - use super::{ geofence_property::{Basic, FullPropertyModel}, sea_orm_active_enums::Type, @@ -27,6 +25,7 @@ use geojson::{GeoJson, Geometry}; use sea_orm::{entity::prelude::*, UpdateResult}; use serde::{Deserialize, Serialize}; use serde_json::json; +use utils::TrimPrecision; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] #[sea_orm(table_name = "geofence")] @@ -233,7 +232,7 @@ impl Model { geometry: Some(if args.internal.is_some() || args.fullcoords.is_some() { geometry } else { - geometry.to_f32() + geometry.trim_precision(6) }), ..Feature::default() }; diff --git a/server/model/src/utils/mod.rs b/server/model/src/utils/mod.rs index 8727e4c0..874986f6 100644 --- a/server/model/src/utils/mod.rs +++ b/server/model/src/utils/mod.rs @@ -15,6 +15,20 @@ use crate::{ pub mod json; pub mod normalize; +pub trait TrimPrecision { + fn trim_precision(self, precision: u32) -> Self; +} + +impl TrimPrecision for f64 { + fn trim_precision(self, precision: u32) -> f64 { + if !self.is_finite() { + return self; + } + let precision_factor = 10u32.pow(precision) as f64; + (self * precision_factor).round() / precision_factor + } +} + pub fn sql_raw(area: &FeatureCollection) -> String { let mut string = "".to_string(); for (i, feature) in area.into_iter().enumerate() { @@ -27,9 +41,9 @@ pub fn sql_raw(area: &FeatureCollection) -> String { let geo = geometry.ensure_first_last(); match geo.value { Value::Polygon(_) | Value::MultiPolygon(_) => { - string = format!("{}{} (lon BETWEEN {} AND {}\nAND lat BETWEEN {} AND {}\nAND ST_CONTAINS(ST_GeomFromGeoJSON('{}', 2, 0), POINT(lon, lat)))", + string = format!("{}{} (\n\tlon BETWEEN {} AND {}\n\tAND lat BETWEEN {} AND {}\n\tAND ST_CONTAINS(\n\t\tST_GeomFromGeoJSON('{}', 2, 0),\n\t\tPOINT(lon, lat)\n\t)\n)", string, - if i == 0 { "" } else { " OR" }, + if i == 0 { "" } else { "\nOR" }, bbox[0], bbox[2], bbox[1], bbox[3], geo.to_string() ); }