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::interpolate_transforms;
10
11use crate::Error::{InvalidTransformId, MissingTransforms, NoChannels};
12use crate::{InterpolationMethod, Transform};
13use chrono::{DateTime, Utc};
14use itertools::Itertools;
15use nalgebra::Isometry3;
16use std::collections::{HashMap, HashSet};
17
18use std::vec;
19
20/// Represents a list of transforms for representing different coordinate frames.
21///
22#[derive(Debug, Default, Clone, PartialEq)]
23pub struct ReferenceFrames {
24    pub(crate) transforms: HashMap<(ChannelId, TransformId), Vec<Transform>>,
25    pub(crate) frame_info: HashMap<FrameId, FrameInfo>,
26    pub(crate) channel_info: HashMap<ChannelId, ChannelInfo>,
27    pub(crate) transform_info: HashMap<TransformId, TransformInfo>,
28}
29
30impl ReferenceFrames {
31    pub fn new(
32        transforms: HashMap<(ChannelId, TransformId), Vec<Transform>>,
33        frame_info: HashMap<FrameId, FrameInfo>,
34        channel_info: HashMap<ChannelId, ChannelInfo>,
35        transform_info: HashMap<TransformId, TransformInfo>,
36    ) -> Result<Self, Error> {
37        if !transforms.is_empty() {
38            // check if all frames are referenced by a transforms
39            for frame in frame_info.keys() {
40                let contained_in_transforms = transforms.keys().any(|(_, transform_id)| {
41                    &transform_id.frame_id == frame || &transform_id.child_frame_id == frame
42                });
43                assert!(
44                    contained_in_transforms,
45                    "No transform is referencing child or parent frame: {frame}"
46                );
47            }
48
49            for (current_id, current_transform) in &transforms {
50                if !current_transform
51                    .windows(2)
52                    .all(|t| t[0].timestamp.timestamp_nanos() < t[1].timestamp.timestamp_nanos())
53                {
54                    return Err(TransformsNotSorted {
55                        channel_id: current_id.0.clone(),
56                        transform_id: current_id.1.clone(),
57                    });
58                }
59            }
60        }
61
62        // sorting the transform vectors by time
63        let mut sorted_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> =
64            HashMap::new();
65        for (current_key, current_transforms) in transforms {
66            let mut current_sorted_transforms = current_transforms.clone();
67            current_sorted_transforms.sort_by_key(|t| t.timestamp);
68
69            sorted_transforms.insert(current_key, current_sorted_transforms);
70        }
71
72        //.all(|transforms| transforms.sort_by_key(|t| t.timestamp));
73
74        /*for transform in transforms.iter() {
75            assert!(
76                frame_info.keys().contains(&transform.frame_id),
77                "Frame with id '{}' is missing",
78                transform.frame_id
79            );
80            assert!(
81                frame_info.keys().contains(&transform.child_frame_id),
82                "Frame with id '{}' is missing",
83                transform.child_frame_id
84            );
85        }*/
86        //for (a, b) in transform.iter().tuple_windows() {
87        //    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);
88        //}
89
90        Ok(Self {
91            transforms: sorted_transforms,
92            frame_info,
93            channel_info,
94            transform_info,
95        })
96    }
97
98    pub fn is_empty(&self) -> bool {
99        self.transforms.is_empty()
100    }
101
102    pub fn frame_info(&self) -> &HashMap<FrameId, FrameInfo> {
103        &self.frame_info
104    }
105
106    pub fn channel_info(&self) -> &HashMap<ChannelId, ChannelInfo> {
107        &self.channel_info
108    }
109
110    pub fn transform_info(&self) -> &HashMap<TransformId, TransformInfo> {
111        &self.transform_info
112    }
113
114    /// Creates a subset of.
115    pub fn get_subset(&self, selected_channel_ids: &[ChannelId]) -> Result<ReferenceFrames, Error> {
116        /*let invalid_channel_ids: Vec<&ChannelId> = selected_channel_ids
117            .iter()
118            .filter(|c| !self.get_channel_ids().contains(c))
119            .collect();
120        if !invalid_channel_ids.is_empty() {
121            let invalid_channel_ids = invalid_channel_ids.into_iter().cloned().collect();
122            return Err(InvalidChannelIds(invalid_channel_ids));
123        }*/
124
125        let all_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> = self
126            .transforms
127            .iter()
128            .filter(|((channel_id, _), _)| selected_channel_ids.contains(channel_id))
129            .map(|((channel_id, transform_id), transforms)| {
130                (
131                    (channel_id.clone(), transform_id.clone()),
132                    transforms.clone(),
133                )
134            })
135            .collect();
136
137        let selected_transform_ids: HashSet<TransformId> =
138            all_transforms.keys().map(|(_, t)| t.clone()).collect();
139
140        let selected_frame_ids: HashSet<FrameId> = selected_transform_ids
141            .iter()
142            .flat_map(|t| [t.frame_id.clone(), t.child_frame_id.clone()])
143            .collect();
144
145        let all_frame_info: HashMap<FrameId, FrameInfo> = self
146            .frame_info
147            .iter()
148            .filter(|(i, _)| selected_frame_ids.contains(i))
149            .map(|(i, f)| (i.clone(), f.clone()))
150            .collect();
151
152        let all_channel_info: HashMap<ChannelId, ChannelInfo> = self
153            .channel_info
154            .iter()
155            .filter(|(channel_id, _)| selected_channel_ids.contains(channel_id))
156            .map(|(i, c)| (i.clone(), c.clone()))
157            .collect();
158
159        let all_transform_info: HashMap<TransformId, TransformInfo> = self
160            .transform_info
161            .iter()
162            .filter(|(i, _)| selected_transform_ids.contains(i))
163            .map(|(i, c)| (i.clone(), c.clone()))
164            .collect();
165
166        let reference_frame = ReferenceFrames::new(
167            all_transforms,
168            all_frame_info,
169            all_channel_info,
170            all_transform_info,
171        )?;
172        Ok(reference_frame)
173    }
174
175    pub fn get_timed_subset(&self, timestamp: &DateTime<Utc>) -> Result<ReferenceFrames, Error> {
176        let all_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> = self
177            .transforms
178            .iter()
179            .map(|((channel_id, transform_id), transforms)| {
180                let interpolation_method = self
181                    .get_interpolation_method(transform_id)
182                    .unwrap_or_default();
183                let isometry =
184                    interpolate_transforms(transforms, &Some(*timestamp), interpolation_method);
185
186                isometry.map(|i| {
187                    (
188                        (channel_id.clone(), transform_id.clone()),
189                        vec![Transform::from(*timestamp, i)],
190                    )
191                })
192            })
193            .collect::<Result<HashMap<(ChannelId, TransformId), Vec<Transform>>, _>>()?;
194
195        let all_transform_info = self
196            .transform_info
197            .keys()
198            .map(|k| {
199                (
200                    k.clone(),
201                    TransformInfo::new(Some(InterpolationMethod::Step)),
202                )
203            })
204            .collect();
205
206        let reference_frame = ReferenceFrames::new(
207            all_transforms,
208            self.frame_info.clone(),
209            self.channel_info.clone(),
210            all_transform_info,
211        )?;
212        Ok(reference_frame)
213    }
214
215    pub fn set_interpolation_method(
216        &mut self,
217        transform_id: TransformId,
218        method: Option<InterpolationMethod>,
219    ) {
220        self.transform_info
221            .entry(transform_id)
222            .or_default()
223            .interpolation_method = method;
224    }
225
226    /*pub fn get_transforms_with_validity_time_frames(
227        &self,
228        channel_id: &ChannelId,
229        transform_id: &TransformId,
230    ) -> Result<Vec<(DateTime<Utc>, Duration)>, Error> {
231        if !self.contains_channel(channel_id) {
232            return Err(InvalidChannelId(channel_id.clone()));
233        }
234
235        let transforms = self
236            .transforms
237            .get(&(channel_id.clone(), transform_id.clone()))
238            .ok_or_else(|| InvalidTransformId(channel_id.clone(), transform_id.clone()))?;
239
240        let interpolation_method = self
241            .get_interpolation_method(transform_id)
242            .unwrap_or_default();
243
244        let a = transforms.windows(2).map(|t| t[0].);
245
246        Ok(vec![])
247    }*/
248
249    pub fn transforms(&self) -> &HashMap<(ChannelId, TransformId), Vec<Transform>> {
250        &self.transforms
251    }
252
253    /// Returns the transforms valid at a specific timestamp.
254    pub fn get_valid_transform(
255        &self,
256        channel_id: &ChannelId,
257        transform_id: &TransformId,
258        timestamp: &Option<DateTime<Utc>>,
259    ) -> Result<Vec<&Transform>, Error> {
260        if !self.contains_channel(channel_id) {
261            return Err(InvalidChannelId(channel_id.clone()));
262        }
263
264        let all_transforms: Vec<&Transform> = self
265            .transforms
266            .get(&(channel_id.clone(), transform_id.clone()))
267            .ok_or_else(|| InvalidTransformId(channel_id.clone(), transform_id.clone()))?
268            .iter()
269            .collect();
270
271        if timestamp.is_none() {
272            return Ok(all_transforms);
273        }
274        let timestamp = timestamp.unwrap();
275
276        let mut time_based_filtered_transforms: Vec<&Transform> = all_transforms
277            .clone()
278            .windows(2)
279            .filter(|t| {
280                t[0].timestamp.timestamp_nanos() <= timestamp.timestamp_nanos()
281                    && timestamp.timestamp_nanos() < t[1].timestamp.timestamp_nanos()
282            })
283            /*.filter(|t| {
284                t[0].duration
285                    .map_or(false, |d| timestamp <= t[0].timestamp + d)
286                    || timestamp.timestamp_nanos() < t[1].timestamp.timestamp_nanos()
287            })*/
288            .map(|t| t[0])
289            .collect();
290
291        if all_transforms.last().unwrap().timestamp <= timestamp {
292            time_based_filtered_transforms.push(all_transforms.last().unwrap());
293        }
294
295        Ok(time_based_filtered_transforms)
296    }
297
298    pub fn get_channel_ids(&self) -> HashSet<ChannelId> {
299        self.transforms
300            .keys()
301            .map(|(channel_id, _)| channel_id.clone())
302            .collect()
303    }
304
305    pub fn get_frame_ids(&self) -> HashSet<FrameId> {
306        self.transforms
307            .keys()
308            .fold(Vec::<FrameId>::new(), |mut acc, x| {
309                acc.push(x.1.frame_id.clone());
310                acc.push(x.1.child_frame_id.clone());
311                acc
312            })
313            .into_iter()
314            .collect()
315    }
316
317    /*pub fn get_channel_names(&self) -> HashSet<ChannelId> {
318        self.transforms
319            .keys()
320            .map(|(channel_id, _)| channel_id.clone())
321            .collect()
322    }*/
323
324    pub fn get_channel_priority(&self, channel_id: &ChannelId) -> i32 {
325        assert!(self.contains_channel(channel_id));
326        self.channel_info
327            .get(channel_id)
328            .and_then(|x| x.priority)
329            .unwrap_or(0)
330    }
331
332    pub fn contains_channel(&self, channel_id: &ChannelId) -> bool {
333        self.get_channel_ids().iter().any(|x| x == channel_id)
334    }
335
336    pub fn contains_frame(&self, frame_id: &FrameId) -> bool {
337        self.get_frame_ids().iter().any(|f| f == frame_id)
338    }
339
340    pub fn contains_transform(&self, channel_id: &ChannelId, transform_id: &TransformId) -> bool {
341        self.transforms
342            .keys()
343            .any(|(current_channel_id, current_transform_id)| {
344                current_channel_id == channel_id && current_transform_id == transform_id
345            })
346    }
347
348    pub fn add_transform(
349        &mut self,
350        channel_id: ChannelId,
351        transform_id: TransformId,
352        transforms: Vec<Transform>,
353        channel_info: Option<ChannelInfo>,
354        transform_info: Option<TransformInfo>,
355    ) -> Result<(), Error> {
356        if transforms.is_empty() {
357            return Err(MissingTransforms());
358        }
359
360        self.transforms
361            .insert((channel_id.clone(), transform_id.clone()), transforms);
362        if let Some(channel_info) = channel_info {
363            self.channel_info.insert(channel_id, channel_info);
364        }
365        if let Some(transform_info) = transform_info {
366            self.transform_info.insert(transform_id, transform_info);
367        }
368        Ok(())
369    }
370
371    pub fn get_interpolation_method(
372        &self,
373        transform_id: &TransformId,
374    ) -> Option<InterpolationMethod> {
375        self.transform_info
376            .get(transform_id)
377            .and_then(|o| o.interpolation_method)
378    }
379
380    /// Derive a concrete transform graph for a specific timestamp and selected channels.
381    ///
382    /// * `selected_channel_ids` - Selected channels for building the transform graph.
383    /// * `selected_timestamp` - Timestamp to choose for interpolating time-dependent transforms.
384    pub fn derive_transform_graph(
385        &self,
386        selected_channel_ids: &Option<HashSet<ChannelId>>,
387        selected_timestamp: &Option<DateTime<Utc>>,
388    ) -> Result<IsometryGraph, Error> {
389        if let Some(channel_names) = &selected_channel_ids {
390            if channel_names.is_empty() {
391                return Err(NoChannels());
392            }
393        }
394
395        let mut selected_isometries: HashMap<TransformId, Isometry3<f64>> = HashMap::new();
396        //if let Some(selected_channel_ids) = selected_channel_ids {
397        //    let a: HashMap<&(ChannelId, TransformId), &Transform> = self.transforms.iter().filter(|&(k, v)| selected_channel_ids.contains(&k.0)).collect();
398        //    println!("x is None")
399        //}
400
401        let selected_transforms: HashMap<(ChannelId, TransformId), Vec<Transform>> =
402            match &selected_channel_ids {
403                Some(channel_names) => filter_by_channel(&self.transforms, channel_names),
404                None => self.transforms.clone(),
405            };
406
407        let mut prioritized_selected_transforms: HashMap<TransformId, Vec<Transform>> =
408            HashMap::new();
409        for (_, group) in &selected_transforms
410            .iter()
411            .sorted_by_key(|k| &k.0 .1)
412            .chunk_by(|k| k.0 .1.clone())
413        {
414            //group.into_iter().for_each(|x| println!("{}, {}", x.0.0, self.get_channel_priority(&x.0.0)));
415            let highest_priority = group
416                .into_iter()
417                .max_by_key(|k| self.get_channel_priority(&k.0 .0))
418                .unwrap();
419
420            //println!("{}", highest_priority.0 .0);
421            prioritized_selected_transforms
422                .insert(highest_priority.0 .1.clone(), highest_priority.1.clone());
423        }
424        //let test = selected_transforms.iter().group_by(|k| k.1);
425
426        for (current_transform_id, current_transforms) in prioritized_selected_transforms {
427            // let key = (current_channel_id.clone(), current_transform_id.clone());
428            let interpolation_method = self
429                .get_interpolation_method(&current_transform_id)
430                .unwrap_or_default();
431            let interpolated_transform = interpolate_transforms(
432                &current_transforms,
433                selected_timestamp,
434                interpolation_method,
435            )?;
436            selected_isometries.insert(current_transform_id.clone(), interpolated_transform);
437
438            //   .entry(current_transform_id.clone())
439            //    .insert_entry(Vec::new)
440            //    .push(interpolated_transform);
441
442            /*
443            #[cfg(debug_assertions)]
444            {
445                let min_max_result = current_transforms.iter().minmax_by_key(|t| t.timestamp);
446                match min_max_result {
447                    MinMaxResult::NoElements => {
448                        println!(
449                            "transform line frame_id={} child_frame_id={}: no elements",
450                            &current_transform_id.frame_id, &current_transform_id.child_frame_id
451                        );
452                    }
453                    MinMaxResult::OneElement(e) => {
454                        println!(
455                            "transform frame_id={} child_frame_id={}: one element={}",
456                            &current_transform_id.frame_id,
457                            &current_transform_id.child_frame_id,
458                            e.timestamp
459                        );
460                    }
461                    MinMaxResult::MinMax(min, max) => {
462                        println!(
463                            "transform frame_id={} child_frame_id={}: min={}, max={}",
464                            &current_transform_id.frame_id,
465                            &current_transform_id.child_frame_id,
466                            min.timestamp,
467                            max.timestamp
468                        );
469                    }
470                }
471            }
472            */
473        }
474
475        IsometryGraph::new(selected_isometries)
476    }
477    //
478    // pub fn get_start_timestamp(
479    //     &self,
480    //     selected_channel_ids: Option<&HashSet<ChannelId>>,
481    // ) -> DateTime<Utc> {
482    //     assert!(selected_channel_ids.is_none(), "Not supported yet.");
483    //     self.transforms
484    //         .iter()
485    //         .flat_map(|t| t.1)
486    //         .map(|t| t.timestamp)
487    //         .min()
488    //         .unwrap()
489    // }
490}