epoint_core/
point_data_columns.rs1use crate::Error::{NoData, ShapeMismatch};
2use crate::{Error, PointDataColumnType};
3use chrono::{DateTime, Timelike, Utc};
4use nalgebra::Point3;
5use palette::Srgb;
6use polars::frame::DataFrame;
7use polars::prelude::{Column, NamedFrom};
8
9pub struct PointDataColumns {
10 pub point: Vec<Point3<f64>>,
11 pub id: Option<Vec<u64>>,
12 pub frame_id: Option<Vec<String>>,
13 pub timestamp: Option<Vec<DateTime<Utc>>>,
14 pub intensity: Option<Vec<f32>>,
15 pub sensor_translation: Option<Vec<Point3<f64>>>,
16 pub color: Option<Vec<Srgb<u16>>>,
17}
18
19impl PointDataColumns {
20 pub fn new(
21 point: Vec<Point3<f64>>,
22 id: Option<Vec<u64>>,
23 frame_id: Option<Vec<String>>,
24 timestamp: Option<Vec<DateTime<Utc>>>,
25 intensity: Option<Vec<f32>>,
26 sensor_translation: Option<Vec<Point3<f64>>>,
27 color: Option<Vec<Srgb<u16>>>,
28 ) -> Result<Self, Error> {
29 if point.is_empty() {
30 return Err(NoData("point"));
31 }
32 let total_length = point.len();
33
34 if let Some(id) = &id
35 && id.len() != total_length
36 {
37 return Err(ShapeMismatch(
38 "id vector has a different length than the point vector",
39 ));
40 }
41 if let Some(frame_id_entries) = &frame_id
42 && frame_id_entries.len() != total_length
43 {
44 return Err(ShapeMismatch(
45 "frame_id vector has a different length than the point vector",
46 ));
47 }
48 if let Some(timestamp_entries) = ×tamp
49 && timestamp_entries.len() != total_length
50 {
51 return Err(ShapeMismatch(
52 "frame_id vector has a different length than the point vector",
53 ));
54 }
55 if let Some(intensity_entries) = &intensity
56 && intensity_entries.len() != total_length
57 {
58 return Err(ShapeMismatch(
59 "intensity vector has a different length than the point vector",
60 ));
61 }
62 if let Some(sensor_translation_entries) = &sensor_translation
63 && sensor_translation_entries.len() != total_length
64 {
65 return Err(ShapeMismatch(
66 "sensor_translation vector has a different length than the point vector",
67 ));
68 }
69 if let Some(color_entries) = &color
70 && color_entries.len() != total_length
71 {
72 return Err(ShapeMismatch(
73 "color vector has a different length than the point vector",
74 ));
75 }
76
77 Ok(Self {
78 point,
79 id,
80 frame_id,
81 timestamp,
82 intensity,
83 sensor_translation,
84 color,
85 })
86 }
87
88 pub fn is_empty(&self) -> bool {
89 self.point.is_empty()
90 }
91
92 pub fn len(&self) -> usize {
93 self.point.len()
94 }
95
96 pub fn get_as_data_frame(&self) -> DataFrame {
97 let mut columns = self.get_xyz_columns();
98
99 if let Some(id) = &self.id {
100 let id_column = Column::new(PointDataColumnType::Id.into(), id);
101 columns.push(id_column);
102 }
103
104 if let Some(frame_id) = &self.frame_id {
105 let frame_id_column = Column::new(PointDataColumnType::FrameId.into(), frame_id);
106 let frame_id_column = frame_id_column
107 .cast(&PointDataColumnType::FrameId.data_frame_data_type())
108 .unwrap();
109 columns.push(frame_id_column);
110 }
111
112 if let Some(timestamp_entries) = &self.timestamp {
113 let timestamp_seconds_column = Column::new(
114 PointDataColumnType::TimestampSecond.into(),
115 timestamp_entries
116 .iter()
117 .map(|t| t.timestamp())
118 .collect::<Vec<i64>>(),
119 );
120 columns.push(timestamp_seconds_column);
121
122 let timestamp_nanoseconds_column = Column::new(
123 PointDataColumnType::TimestampNanoSecond.into(),
124 timestamp_entries
125 .iter()
126 .map(|t| t.nanosecond())
127 .collect::<Vec<u32>>(),
128 );
129 columns.push(timestamp_nanoseconds_column);
130 }
131
132 if let Some(intensity) = &self.intensity {
133 let intensity_column = Column::new(PointDataColumnType::Intensity.into(), intensity);
134 columns.push(intensity_column);
135 }
136
137 if self.sensor_translation.is_some() {
138 columns.append(&mut self.get_sensor_translation_xyz_columns().unwrap());
139 }
140
141 if self.color.is_some() {
142 columns.append(&mut self.get_color_columns().unwrap());
143 }
144
145 DataFrame::new(columns).unwrap()
146 }
147
148 pub fn get_xyz_columns(&self) -> Vec<Column> {
149 vec![
150 Column::new(
151 PointDataColumnType::X.into(),
152 self.point.iter().map(|p| p.x).collect::<Vec<f64>>(),
153 ),
154 Column::new(
155 PointDataColumnType::Y.into(),
156 self.point.iter().map(|p| p.y).collect::<Vec<f64>>(),
157 ),
158 Column::new(
159 PointDataColumnType::Z.into(),
160 self.point.iter().map(|p| p.z).collect::<Vec<f64>>(),
161 ),
162 ]
163 }
164
165 pub fn get_sensor_translation_xyz_columns(&self) -> Option<Vec<Column>> {
166 let sensor_translation = self.sensor_translation.as_ref()?;
167
168 Some(vec![
169 Column::new(
170 PointDataColumnType::SensorTranslationX.into(),
171 sensor_translation.iter().map(|p| p.x).collect::<Vec<f64>>(),
172 ),
173 Column::new(
174 PointDataColumnType::SensorTranslationY.into(),
175 sensor_translation.iter().map(|p| p.y).collect::<Vec<f64>>(),
176 ),
177 Column::new(
178 PointDataColumnType::SensorTranslationZ.into(),
179 sensor_translation.iter().map(|p| p.z).collect::<Vec<f64>>(),
180 ),
181 ])
182 }
183
184 pub fn get_color_columns(&self) -> Option<Vec<Column>> {
185 let color = self.color.as_ref()?;
186
187 Some(vec![
188 Column::new(
189 PointDataColumnType::ColorRed.into(),
190 color.iter().map(|c| c.red).collect::<Vec<u16>>(),
191 ),
192 Column::new(
193 PointDataColumnType::ColorGreen.into(),
194 color.iter().map(|c| c.green).collect::<Vec<u16>>(),
195 ),
196 Column::new(
197 PointDataColumnType::ColorBlue.into(),
198 color.iter().map(|c| c.blue).collect::<Vec<u16>>(),
199 ),
200 ])
201 }
202}