egml_core/model/geometry/aggregates/
multi_curve.rs1use crate::model::geometry::Envelope;
2use crate::model::geometry::aggregates::{
3 AbstractGeometricAggregate, AsAbstractGeometricAggregate, AsAbstractGeometricAggregateMut,
4};
5use crate::model::geometry::primitives::CurveProperty;
6use crate::{Error, impl_abstract_geometric_aggregate_traits};
7use nalgebra::Isometry3;
8use rayon::iter::IntoParallelRefMutIterator;
9use rayon::iter::ParallelIterator;
10
11#[derive(Debug, Clone, PartialEq)]
15pub struct MultiCurve {
16 pub(crate) abstract_geometric_aggregate: AbstractGeometricAggregate,
17 curve_member: Vec<CurveProperty>,
18}
19
20impl MultiCurve {
21 pub fn new(members: impl IntoIterator<Item = CurveProperty>) -> Result<Self, Error> {
27 let members: Vec<CurveProperty> = members.into_iter().collect();
28 if members.is_empty() {
29 return Err(Error::TooFewElements {
30 geometry: "gml:MultiCurve",
31 minimum: 1,
32 spec: Some("OGC 07-036 §11.3.3.1"),
33 id: None,
34 detail: None,
35 });
36 }
37
38 Ok(Self {
39 abstract_geometric_aggregate: AbstractGeometricAggregate::default(),
40 curve_member: members,
41 })
42 }
43
44 pub fn curve_member(&self) -> &[CurveProperty] {
46 &self.curve_member
47 }
48
49 pub fn set_curve_member(&mut self, val: Vec<CurveProperty>) -> Result<(), Error> {
55 if val.is_empty() {
56 return Err(Error::TooFewElements {
57 geometry: "gml:MultiCurve",
58 minimum: 1,
59 spec: Some("OGC 07-036 §11.3.3.1"),
60 id: None,
61 detail: None,
62 });
63 }
64 self.curve_member = val;
65 Ok(())
66 }
67
68 pub fn push_curve_member(&mut self, member: CurveProperty) {
69 self.curve_member.push(member);
70 }
71
72 pub fn extend_curve_members(&mut self, members: impl IntoIterator<Item = CurveProperty>) {
73 self.curve_member.extend(members);
74 }
75}
76
77impl MultiCurve {
78 pub fn length_3d(&self) -> Result<f64, Error> {
85 self.curve_member
86 .iter()
87 .map(|c| {
88 c.object
89 .as_ref()
90 .ok_or_else(|| Error::UnresolvedCurveReference {
91 href: c.href.clone(),
92 })
93 .map(|curve| curve.length_3d())
94 })
95 .collect::<Result<Vec<f64>, Error>>()
96 .map(|lengths| lengths.into_iter().sum())
97 }
98
99 pub fn apply_transform(&mut self, m: &Isometry3<f64>) {
100 self.curve_member.par_iter_mut().for_each(|p| {
101 if let Some(object) = &mut p.object {
102 object.apply_transform(m);
103 }
104 });
105 }
106
107 pub fn compute_envelope(&self) -> Option<Envelope> {
109 let envelopes: Vec<Envelope> = self
110 .curve_member
111 .iter()
112 .flat_map(|x| x.object.as_ref())
113 .map(|x| x.compute_envelope())
114 .collect();
115
116 Envelope::from_envelopes(&envelopes)
117 }
118}
119
120impl AsAbstractGeometricAggregate for MultiCurve {
121 fn abstract_geometric_aggregate(&self) -> &AbstractGeometricAggregate {
122 &self.abstract_geometric_aggregate
123 }
124}
125
126impl AsAbstractGeometricAggregateMut for MultiCurve {
127 fn abstract_geometric_aggregate_mut(&mut self) -> &mut AbstractGeometricAggregate {
128 &mut self.abstract_geometric_aggregate
129 }
130}
131
132impl_abstract_geometric_aggregate_traits!(MultiCurve);
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::Error;
138 use crate::model::geometry::DirectPosition;
139 use crate::model::geometry::primitives::{CurveKind, LineString};
140
141 fn line_string(points: Vec<DirectPosition>) -> CurveProperty {
142 CurveProperty::new(CurveKind::LineString(LineString::new(points).unwrap()))
143 }
144
145 #[test]
146 fn length_3d_two_segments() {
147 let multi_curve = MultiCurve::new([
149 line_string(vec![
150 DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
151 DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
152 ]),
153 line_string(vec![
154 DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
155 DirectPosition::new(2.0, 0.0, 0.0).unwrap(),
156 ]),
157 ])
158 .unwrap();
159 assert!((multi_curve.length_3d().unwrap() - 2.0).abs() < 1e-10);
160 }
161
162 #[test]
163 fn length_3d_unresolved_curve_reference() {
164 let multi_curve =
165 MultiCurve::new([CurveProperty::new_href("urn:example:curve-1")]).unwrap();
166 assert_eq!(
167 multi_curve.length_3d(),
168 Err(Error::UnresolvedCurveReference {
169 href: Some("urn:example:curve-1".to_string())
170 })
171 );
172 }
173}