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