ecoord_core/
reference_frames.rs

1use crate::channel_info::{ChannelId, ChannelInfo};
2use crate::error::Error;
3use crate::error::Error::{InvalidChannelId, TransformsNotSorted};
4use crate::frame_info::{FrameId, FrameInfo};
5use crate::isometry_graph::IsometryGraph;
6use crate::ops::filter::filter_by_channel;
7use crate::transform::TransformId;
8use crate::transform_info::TransformInfo;
9use crate::utils::transforms_interpolation::inter_and_extrapolate_transforms;
10
11use crate::Error::{InvalidTransformId, NoChannels, NoTransforms};
12use crate::{ExtrapolationMethod, InterpolationMethod, Transform};
13use chrono::{DateTime, Utc};
14use itertools::Itertools;
15use nalgebra::Isometry3;
16use rayon::iter::ParallelIterator;
17use rayon::prelude::IntoParallelRefIterator;
18use std::collections::{HashMap, HashSet};
19use std::vec;
20
21/// Represents a list of transforms for representing different coordinate frames.
22///
23#[derive(Debug, Default, Clone, PartialEq)]
24pub struct ReferenceFrames {
25    pub transforms: HashMap<(ChannelId, TransformId), Vec<Transform>>,
26    pub frame_info: HashMap<FrameId, FrameInfo>,
27    pub channel_info: HashMap<ChannelId, ChannelInfo>,
28    pub transform_info: HashMap<TransformId, TransformInfo>,
29}
30
31impl ReferenceFrames {
32    pub fn new(
33        transforms: HashMap<(ChannelId, TransformId), Vec<Transform>>,
34        frame_info: HashMap<FrameId, FrameInfo>,
35        channel_info: HashMap<ChannelId, ChannelInfo>,
36        transform_info: HashMap<TransformId, TransformInfo>,
37    ) -> Result<Self, Error> {
38        if !transforms.is_empty() {
39            // check if all frames are referenced by a transforms
40            for frame in frame_info.keys() {
41                let contained_in_transforms = transforms.keys().any(|(_, transform_id)| {
42                    &transform_id.frame_id == frame || &transform_id.child_frame_id == frame
43                });
44                assert!(
45                    contained_in_transforms,
46                    "No transform is referencing child or parent frame: {frame}"
47                );
48            }
49
50            for (current_id, current_transform) in &transforms {
51                if !current_transform.windows(2).all(|t| {
52                    t[0].timestamp
53                        .timestamp_nanos_opt()
54                        .expect("should be defined")
55                        < t[1]
56                            .timestamp
57                            .timestamp_nanos_opt()
58                            .expect("should be defined")
59                }) {
60                    return Err(TransformsNotSorted {
61                        channel_id: current_id.0.clone(),
62                        transform_id: current_id.1.clone(),
63                    });
64                }
65            }
66        }
67
68        // sorting the transform vectors by time
69        let mut sorted_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> =
70            HashMap::new();
71        for (current_key, current_transforms) in transforms {
72            let mut current_sorted_transforms = current_transforms.clone();
73            current_sorted_transforms.sort_by_key(|t| t.timestamp);
74
75            sorted_transforms.insert(current_key, current_sorted_transforms);
76        }
77
78        //.all(|transforms| transforms.sort_by_key(|t| t.timestamp));
79
80        /*for transform in transforms.iter() {
81            assert!(
82                frame_info.keys().contains(&transform.frame_id),
83                "Frame with id '{}' is missing",
84                transform.frame_id
85            );
86            assert!(
87                frame_info.keys().contains(&transform.child_frame_id),
88                "Frame with id '{}' is missing",
89                transform.child_frame_id
90            );
91        }*/
92        //for (a, b) in transform.iter().tuple_windows() {
93        //    assert_eq!(a.child_frame_id, b.parent_frame_id, "Child frame '{}' does not fit to parent frame '{}' of the following transform", a.child_frame_id, b.parent_frame_id);
94        //}
95
96        Ok(Self {
97            transforms: sorted_transforms,
98            frame_info,
99            channel_info,
100            transform_info,
101        })
102    }
103
104    pub fn is_empty(&self) -> bool {
105        self.transforms.is_empty()
106    }
107
108    pub fn transforms(&self) -> &HashMap<(ChannelId, TransformId), Vec<Transform>> {
109        &self.transforms
110    }
111
112    pub fn frame_info(&self) -> &HashMap<FrameId, FrameInfo> {
113        &self.frame_info
114    }
115
116    pub fn channel_info(&self) -> &HashMap<ChannelId, ChannelInfo> {
117        &self.channel_info
118    }
119
120    pub fn transform_info(&self) -> &HashMap<TransformId, TransformInfo> {
121        &self.transform_info
122    }
123}
124
125impl ReferenceFrames {
126    pub fn get_channel_ids(&self) -> HashSet<ChannelId> {
127        self.transforms
128            .keys()
129            .map(|(channel_id, _)| channel_id.clone())
130            .collect()
131    }
132
133    pub fn get_frame_ids(&self) -> HashSet<FrameId> {
134        self.transforms
135            .keys()
136            .fold(Vec::<FrameId>::new(), |mut acc, x| {
137                acc.push(x.1.frame_id.clone());
138                acc.push(x.1.child_frame_id.clone());
139                acc
140            })
141            .into_iter()
142            .collect()
143    }
144
145    pub fn get_transforms_of_channel(
146        &self,
147        channel_id: &ChannelId,
148        transform_id: &TransformId,
149    ) -> Result<&Vec<Transform>, Error> {
150        let transforms = self
151            .transforms
152            .get(&(channel_id.clone(), transform_id.clone()))
153            .ok_or(NoTransforms())?;
154        Ok(transforms)
155    }
156
157    pub fn get_channel_priority(&self, channel_id: &ChannelId) -> Result<i32, Error> {
158        if !self.get_channel_ids().contains(channel_id) {
159            return Err(InvalidChannelId(channel_id.clone()));
160        }
161
162        let priority = self.channel_info.get(channel_id).and_then(|x| x.priority);
163        Ok(priority.unwrap_or_default())
164    }
165
166    pub fn get_interpolation_method(&self, transform_id: &TransformId) -> InterpolationMethod {
167        self.transform_info
168            .get(transform_id)
169            .map(|x| x.interpolation_method)
170            .unwrap_or_default()
171    }
172
173    pub fn get_extrapolation_method(&self, transform_id: &TransformId) -> ExtrapolationMethod {
174        self.transform_info
175            .get(transform_id)
176            .map(|x| x.extrapolation_method)
177            .unwrap_or_default()
178    }
179
180    pub fn contains_channel(&self, channel_id: &ChannelId) -> bool {
181        self.get_channel_ids().iter().any(|x| x == channel_id)
182    }
183
184    pub fn contains_frame(&self, frame_id: &FrameId) -> bool {
185        self.get_frame_ids().iter().any(|f| f == frame_id)
186    }
187
188    pub fn contains_transform(&self, channel_id: &ChannelId, transform_id: &TransformId) -> bool {
189        self.transforms
190            .keys()
191            .any(|(current_channel_id, current_transform_id)| {
192                current_channel_id == channel_id && current_transform_id == transform_id
193            })
194    }
195}
196
197impl ReferenceFrames {
198    pub fn set_interpolation_method(
199        &mut self,
200        transform_id: TransformId,
201        method: InterpolationMethod,
202    ) {
203        self.transform_info
204            .entry(transform_id)
205            .or_default()
206            .interpolation_method = method;
207    }
208
209    pub fn set_extrapolation_method(
210        &mut self,
211        transform_id: TransformId,
212        method: ExtrapolationMethod,
213    ) {
214        self.transform_info
215            .entry(transform_id)
216            .or_default()
217            .extrapolation_method = method;
218    }
219
220    pub fn add_transforms(
221        &mut self,
222        channel_id: ChannelId,
223        transform_id: TransformId,
224        transforms: Vec<Transform>,
225        channel_info: Option<ChannelInfo>,
226        transform_info: Option<TransformInfo>,
227    ) -> Result<(), Error> {
228        if transforms.is_empty() {
229            return Err(NoTransforms());
230        }
231
232        self.transforms
233            .insert((channel_id.clone(), transform_id.clone()), transforms);
234        if let Some(channel_info) = channel_info {
235            self.channel_info.insert(channel_id, channel_info);
236        }
237        if let Some(transform_info) = transform_info {
238            self.transform_info.insert(transform_id, transform_info);
239        }
240        Ok(())
241    }
242
243    // Extend transforms into already existing
244    pub fn extend_transforms(
245        &mut self,
246        channel_id: ChannelId,
247        transform_id: TransformId,
248        transforms: Vec<Transform>,
249    ) -> Result<(), Error> {
250        if transforms.is_empty() {
251            return Err(NoTransforms());
252        }
253        let key = (channel_id, transform_id);
254
255        let mut combined_transforms: Vec<Transform> =
256            self.transforms.get(&key).map_or(Vec::new(), |x| x.clone());
257        combined_transforms.extend(transforms);
258
259        combined_transforms.sort_by_key(|t| t.timestamp);
260
261        self.transforms.insert(key, combined_transforms);
262
263        Ok(())
264    }
265}
266
267impl ReferenceFrames {
268    /// Filter by channel ids
269    pub fn filter_by_channel_ids(
270        &self,
271        channel_ids: &[ChannelId],
272    ) -> Result<ReferenceFrames, Error> {
273        let all_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> = self
274            .transforms
275            .par_iter()
276            .filter(|((channel_id, _), _)| channel_ids.contains(channel_id))
277            .map(|((channel_id, transform_id), transforms)| {
278                (
279                    (channel_id.clone(), transform_id.clone()),
280                    transforms.clone(),
281                )
282            })
283            .collect();
284
285        let selected_transform_ids: HashSet<TransformId> =
286            all_transforms.keys().map(|(_, t)| t.clone()).collect();
287        let selected_frame_ids: HashSet<FrameId> = selected_transform_ids
288            .par_iter()
289            .flat_map(|t| [t.frame_id.clone(), t.child_frame_id.clone()])
290            .collect();
291
292        let all_frame_info: HashMap<FrameId, FrameInfo> = self
293            .frame_info
294            .par_iter()
295            .filter(|(i, _)| selected_frame_ids.contains(i))
296            .map(|(i, f)| (i.clone(), f.clone()))
297            .collect();
298        let all_channel_info: HashMap<ChannelId, ChannelInfo> = self
299            .channel_info
300            .par_iter()
301            .filter(|(channel_id, _)| channel_ids.contains(channel_id))
302            .map(|(i, c)| (i.clone(), c.clone()))
303            .collect();
304        let all_transform_info: HashMap<TransformId, TransformInfo> = self
305            .transform_info
306            .par_iter()
307            .filter(|(i, _)| selected_transform_ids.contains(i))
308            .map(|(i, c)| (i.clone(), c.clone()))
309            .collect();
310
311        let reference_frame = ReferenceFrames::new(
312            all_transforms,
313            all_frame_info,
314            all_channel_info,
315            all_transform_info,
316        )?;
317        Ok(reference_frame)
318    }
319
320    pub fn filter_by_transform_ids(
321        &self,
322        transform_ids: &[TransformId],
323    ) -> Result<ReferenceFrames, Error> {
324        let all_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> = self
325            .transforms
326            .par_iter()
327            .filter(|((_, transform_id), _)| transform_ids.contains(transform_id))
328            .map(|((channel_id, transform_id), transforms)| {
329                (
330                    (channel_id.clone(), transform_id.clone()),
331                    transforms.clone(),
332                )
333            })
334            .collect();
335
336        let selected_channel_ids: HashSet<ChannelId> =
337            all_transforms.keys().map(|(c, _)| c.clone()).collect();
338        let selected_transform_ids: HashSet<TransformId> =
339            all_transforms.keys().map(|(_, t)| t.clone()).collect();
340        let selected_frame_ids: HashSet<FrameId> = selected_transform_ids
341            .par_iter()
342            .flat_map(|t| [t.frame_id.clone(), t.child_frame_id.clone()])
343            .collect();
344
345        let all_frame_info: HashMap<FrameId, FrameInfo> = self
346            .frame_info
347            .par_iter()
348            .filter(|(i, _)| selected_frame_ids.contains(i))
349            .map(|(i, f)| (i.clone(), f.clone()))
350            .collect();
351        let all_channel_info: HashMap<ChannelId, ChannelInfo> = self
352            .channel_info
353            .par_iter()
354            .filter(|(channel_id, _)| selected_channel_ids.contains(channel_id))
355            .map(|(i, c)| (i.clone(), c.clone()))
356            .collect();
357        let all_transform_info: HashMap<TransformId, TransformInfo> = self
358            .transform_info
359            .par_iter()
360            .filter(|(i, _)| selected_transform_ids.contains(i))
361            .map(|(i, c)| (i.clone(), c.clone()))
362            .collect();
363
364        let reference_frame = ReferenceFrames::new(
365            all_transforms,
366            all_frame_info,
367            all_channel_info,
368            all_transform_info,
369        )?;
370        Ok(reference_frame)
371    }
372
373    pub fn filter_by_time_interval(
374        &self,
375        start_time: &Option<DateTime<Utc>>,
376        stop_time: &Option<DateTime<Utc>>,
377    ) -> Result<ReferenceFrames, Error> {
378        if start_time.is_none() && stop_time.is_none() {
379            return Ok(self.clone());
380        }
381
382        let all_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> = self
383            .transforms
384            .par_iter()
385            .flat_map(|((channel_id, transform_id), transforms)| {
386                let filtered_transforms: Vec<Transform> = transforms
387                    .iter()
388                    .filter(|t| start_time.is_none_or(|x| x <= t.timestamp))
389                    .filter(|t| stop_time.is_none_or(|x| t.timestamp < x))
390                    .cloned()
391                    .collect();
392                if filtered_transforms.is_empty() {
393                    return None;
394                }
395
396                let key = (channel_id.clone(), transform_id.clone());
397                Some((key, filtered_transforms))
398            })
399            .collect();
400
401        let selected_channel_ids: HashSet<ChannelId> =
402            all_transforms.keys().map(|(c, _)| c.clone()).collect();
403        let selected_transform_ids: HashSet<TransformId> =
404            all_transforms.keys().map(|(_, t)| t.clone()).collect();
405        let selected_frame_ids: HashSet<FrameId> = selected_transform_ids
406            .par_iter()
407            .flat_map(|t| [t.frame_id.clone(), t.child_frame_id.clone()])
408            .collect();
409
410        let all_frame_info: HashMap<FrameId, FrameInfo> = self
411            .frame_info
412            .par_iter()
413            .filter(|(i, _)| selected_frame_ids.contains(i))
414            .map(|(i, f)| (i.clone(), f.clone()))
415            .collect();
416        let all_channel_info: HashMap<ChannelId, ChannelInfo> = self
417            .channel_info
418            .par_iter()
419            .filter(|(channel_id, _)| selected_channel_ids.contains(channel_id))
420            .map(|(i, c)| (i.clone(), c.clone()))
421            .collect();
422        let all_transform_info: HashMap<TransformId, TransformInfo> = self
423            .transform_info
424            .par_iter()
425            .filter(|(i, _)| selected_transform_ids.contains(i))
426            .map(|(i, c)| (i.clone(), c.clone()))
427            .collect();
428
429        let reference_frame = ReferenceFrames::new(
430            all_transforms,
431            all_frame_info,
432            all_channel_info,
433            all_transform_info,
434        )?;
435        Ok(reference_frame)
436    }
437}
438
439impl ReferenceFrames {
440    pub fn get_timed_subset(&self, timestamp: &DateTime<Utc>) -> Result<ReferenceFrames, Error> {
441        let all_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> = self
442            .transforms
443            .iter()
444            .map(|((channel_id, transform_id), transforms)| {
445                let isometry = inter_and_extrapolate_transforms(
446                    transforms,
447                    &Some(*timestamp),
448                    self.get_interpolation_method(transform_id),
449                    self.get_extrapolation_method(transform_id),
450                );
451
452                isometry.map(|i| {
453                    (
454                        (channel_id.clone(), transform_id.clone()),
455                        vec![Transform::from(*timestamp, i)],
456                    )
457                })
458            })
459            .collect::<Result<HashMap<(ChannelId, TransformId), Vec<Transform>>, _>>()?;
460
461        let all_transform_info = self
462            .transform_info
463            .keys()
464            .map(|k| {
465                (
466                    k.clone(),
467                    TransformInfo::new(InterpolationMethod::Step, ExtrapolationMethod::default()),
468                )
469            })
470            .collect();
471
472        let reference_frame = ReferenceFrames::new(
473            all_transforms,
474            self.frame_info.clone(),
475            self.channel_info.clone(),
476            all_transform_info,
477        )?;
478        Ok(reference_frame)
479    }
480
481    /// Returns the transforms valid at a specific timestamp.
482    pub fn get_valid_transform(
483        &self,
484        channel_id: &ChannelId,
485        transform_id: &TransformId,
486        timestamp: &Option<DateTime<Utc>>,
487    ) -> Result<Vec<&Transform>, Error> {
488        if !self.contains_channel(channel_id) {
489            return Err(InvalidChannelId(channel_id.clone()));
490        }
491
492        let all_transforms: Vec<&Transform> = self
493            .transforms
494            .get(&(channel_id.clone(), transform_id.clone()))
495            .ok_or_else(|| InvalidTransformId(channel_id.clone(), transform_id.clone()))?
496            .iter()
497            .collect();
498
499        if timestamp.is_none() {
500            return Ok(all_transforms);
501        }
502        let timestamp = timestamp.unwrap();
503
504        let mut time_based_filtered_transforms: Vec<&Transform> = all_transforms
505            .clone()
506            .windows(2)
507            .filter(|t| {
508                t[0].timestamp.timestamp_nanos_opt().unwrap()
509                    <= timestamp.timestamp_nanos_opt().unwrap()
510                    && timestamp.timestamp_nanos_opt().unwrap()
511                        < t[1].timestamp.timestamp_nanos_opt().unwrap()
512            })
513            /*.filter(|t| {
514                t[0].duration
515                    .map_or(false, |d| timestamp <= t[0].timestamp + d)
516                    || timestamp.timestamp_nanos() < t[1].timestamp.timestamp_nanos()
517            })*/
518            .map(|t| t[0])
519            .collect();
520
521        if all_transforms.last().unwrap().timestamp <= timestamp {
522            time_based_filtered_transforms.push(all_transforms.last().unwrap());
523        }
524
525        Ok(time_based_filtered_transforms)
526    }
527
528    /// Derive a concrete transform graph for a specific timestamp and selected channels.
529    ///
530    /// * `selected_channel_ids` - Selected channels for building the transform graph.
531    /// * `selected_timestamp` - Timestamp to choose for interpolating time-dependent transforms.
532    pub fn derive_transform_graph(
533        &self,
534        selected_channel_ids: &Option<HashSet<ChannelId>>,
535        selected_timestamp: &Option<DateTime<Utc>>,
536    ) -> Result<IsometryGraph, Error> {
537        if let Some(channel_names) = &selected_channel_ids {
538            if channel_names.is_empty() {
539                return Err(NoChannels());
540            }
541        }
542
543        let mut selected_isometries: HashMap<TransformId, Isometry3<f64>> = HashMap::new();
544        let selected_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> =
545            match &selected_channel_ids {
546                Some(channel_names) => filter_by_channel(&self.transforms, channel_names),
547                None => self.transforms.clone(),
548            };
549
550        let mut prioritized_selected_transforms: HashMap<TransformId, Vec<Transform>> =
551            HashMap::new();
552        for (_, group) in &selected_transforms
553            .iter()
554            .sorted_by_key(|k| &k.0.1)
555            .chunk_by(|k| k.0.1.clone())
556        {
557            let highest_priority = group
558                .into_iter()
559                .max_by_key(|k| {
560                    self.get_channel_priority(&k.0.0)
561                        .expect("channel should exist")
562                })
563                .unwrap();
564
565            prioritized_selected_transforms
566                .insert(highest_priority.0.1.clone(), highest_priority.1.clone());
567        }
568
569        for (current_transform_id, current_transforms) in prioritized_selected_transforms {
570            let interpolated_transform = inter_and_extrapolate_transforms(
571                &current_transforms,
572                selected_timestamp,
573                self.get_interpolation_method(&current_transform_id),
574                self.get_extrapolation_method(&current_transform_id),
575            )?;
576            selected_isometries.insert(current_transform_id.clone(), interpolated_transform);
577        }
578
579        IsometryGraph::new(selected_isometries)
580    }
581}