egml_core/model/geometry/aggregates/
multi_curve.rs1use crate::model::base::HasAssociationAttributes;
2use crate::model::common::{ApplyTransform, ComputeEnvelope, IterGeometries};
3use crate::model::geometry::Envelope;
4use crate::model::geometry::aggregates::{
5 AbstractGeometricAggregate, AsAbstractGeometricAggregate, AsAbstractGeometricAggregateMut,
6};
7use crate::model::geometry::primitives::AbstractCurveProperty;
8use crate::model::geometry::refs::AbstractGeometryKindRef;
9use crate::{
10 Error, impl_abstract_geometric_aggregate_mut_traits, impl_abstract_geometric_aggregate_traits,
11 impl_has_geometry_type,
12};
13use nalgebra::{Isometry3, Rotation3, Scale3, Transform3, Vector3};
14use rayon::iter::IntoParallelRefMutIterator;
15use rayon::iter::ParallelIterator;
16
17#[derive(Debug, Clone, PartialEq)]
21pub struct MultiCurve {
22 pub abstract_geometric_aggregate: AbstractGeometricAggregate,
23 curve_member: Vec<AbstractCurveProperty>,
24}
25
26impl MultiCurve {
27 pub fn new(members: impl IntoIterator<Item = AbstractCurveProperty>) -> Result<Self, Error> {
33 let members: Vec<AbstractCurveProperty> = members.into_iter().collect();
34 Self::validate(&members)?;
35
36 Ok(Self {
37 abstract_geometric_aggregate: AbstractGeometricAggregate::default(),
38 curve_member: members,
39 })
40 }
41
42 pub fn from_abstract_geometric_aggregate(
43 abstract_geometric_aggregate: AbstractGeometricAggregate,
44 members: impl IntoIterator<Item = AbstractCurveProperty>,
45 ) -> Result<Self, Error> {
46 let members: Vec<AbstractCurveProperty> = members.into_iter().collect();
47 Self::validate(&members)?;
48
49 Ok(Self {
50 abstract_geometric_aggregate,
51 curve_member: members,
52 })
53 }
54
55 fn validate(members: &[AbstractCurveProperty]) -> Result<(), Error> {
56 if members.is_empty() {
57 return Err(Error::TooFewElements {
58 geometry: "gml:MultiCurve",
59 minimum: 1,
60 spec: Some("OGC 07-036 §11.3.3.1"),
61 id: None,
62 detail: None,
63 });
64 }
65 Ok(())
66 }
67
68 pub fn curve_member(&self) -> &[AbstractCurveProperty] {
70 &self.curve_member
71 }
72
73 pub fn set_curve_member(&mut self, val: Vec<AbstractCurveProperty>) -> Result<(), Error> {
79 Self::validate(&val)?;
80 self.curve_member = val;
81 Ok(())
82 }
83
84 pub fn push_curve_member(&mut self, member: AbstractCurveProperty) {
85 self.curve_member.push(member);
86 }
87
88 pub fn extend_curve_members(
89 &mut self,
90 members: impl IntoIterator<Item = AbstractCurveProperty>,
91 ) {
92 self.curve_member.extend(members);
93 }
94}
95
96impl MultiCurve {
97 pub fn length_3d(&self) -> Result<f64, Error> {
104 self.curve_member
105 .iter()
106 .map(|c| {
107 c.object()
108 .ok_or_else(|| Error::UnresolvedCurveReference {
109 href: c.href().as_ref().map(|h| h.to_string()),
110 })
111 .map(|curve| curve.length_3d())
112 })
113 .collect::<Result<Vec<f64>, Error>>()
114 .map(|lengths| lengths.into_iter().sum())
115 }
116}
117
118impl ApplyTransform for MultiCurve {
119 fn apply_transform(&mut self, transform: Transform3<f64>) {
120 self.curve_member.par_iter_mut().for_each(|p| {
121 if let Some(object) = p.object_mut() {
122 object.apply_transform(transform);
123 }
124 });
125 }
126
127 fn apply_isometry(&mut self, isometry: Isometry3<f64>) {
128 self.curve_member.par_iter_mut().for_each(|p| {
129 if let Some(object) = p.object_mut() {
130 object.apply_isometry(isometry);
131 }
132 });
133 }
134
135 fn apply_translation(&mut self, vector: Vector3<f64>) {
136 self.curve_member.par_iter_mut().for_each(|p| {
137 if let Some(object) = p.object_mut() {
138 object.apply_translation(vector);
139 }
140 });
141 }
142
143 fn apply_rotation(&mut self, rotation: Rotation3<f64>) {
144 self.curve_member.par_iter_mut().for_each(|p| {
145 if let Some(object) = p.object_mut() {
146 object.apply_rotation(rotation);
147 }
148 });
149 }
150
151 fn apply_scale(&mut self, scale: Scale3<f64>) {
152 self.curve_member.par_iter_mut().for_each(|p| {
153 if let Some(object) = p.object_mut() {
154 object.apply_scale(scale);
155 }
156 });
157 }
158}
159
160impl ComputeEnvelope for MultiCurve {
161 fn compute_envelope(&self) -> Option<Envelope> {
163 let envelopes: Vec<Envelope> = self
164 .curve_member
165 .iter()
166 .flat_map(|x| x.object())
167 .filter_map(|x| x.compute_envelope())
168 .collect();
169
170 Envelope::from_envelopes(&envelopes)
171 }
172}
173
174impl AsAbstractGeometricAggregate for MultiCurve {
175 fn abstract_geometric_aggregate(&self) -> &AbstractGeometricAggregate {
176 &self.abstract_geometric_aggregate
177 }
178}
179
180impl AsAbstractGeometricAggregateMut for MultiCurve {
181 fn abstract_geometric_aggregate_mut(&mut self) -> &mut AbstractGeometricAggregate {
182 &mut self.abstract_geometric_aggregate
183 }
184}
185
186impl_abstract_geometric_aggregate_traits!(MultiCurve);
187impl_abstract_geometric_aggregate_mut_traits!(MultiCurve);
188impl_has_geometry_type!(MultiCurve, MultiCurve);
189
190impl IterGeometries for MultiCurve {
191 fn iter_geometries(&self) -> Box<dyn Iterator<Item = AbstractGeometryKindRef<'_>> + '_> {
192 Box::new(
193 std::iter::once(self.into()).chain(
194 self.curve_member
195 .iter()
196 .filter_map(|x| x.object())
197 .flat_map(|x| x.iter_geometries()),
198 ),
199 )
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use crate::Error;
207 use crate::model::geometry::DirectPosition;
208 use crate::model::geometry::primitives::{AbstractCurveKind, LineString};
209
210 fn line_string(points: Vec<DirectPosition>) -> AbstractCurveProperty {
211 AbstractCurveProperty::from_object(AbstractCurveKind::LineString(
212 LineString::new(points).unwrap(),
213 ))
214 }
215
216 #[test]
217 fn length_3d_two_segments() {
218 let multi_curve = MultiCurve::new([
220 line_string(vec![
221 DirectPosition::new(0.0, 0.0, 0.0).unwrap(),
222 DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
223 ]),
224 line_string(vec![
225 DirectPosition::new(1.0, 0.0, 0.0).unwrap(),
226 DirectPosition::new(2.0, 0.0, 0.0).unwrap(),
227 ]),
228 ])
229 .unwrap();
230 assert!((multi_curve.length_3d().unwrap() - 2.0).abs() < 1e-10);
231 }
232
233 #[test]
234 fn length_3d_unresolved_curve_reference() {
235 let multi_curve = MultiCurve::new([AbstractCurveProperty::from_href(
236 "urn:example:curve-1".into(),
237 )])
238 .unwrap();
239 assert_eq!(
240 multi_curve.length_3d(),
241 Err(Error::UnresolvedCurveReference {
242 href: Some("urn:example:curve-1".to_string())
243 })
244 );
245 }
246}