1use crate::xml;
2use crate::{Blob, DateTime, Error, Result, Transform};
3use roxmltree::{Document, Node};
4use std::f64::consts::PI;
5
6#[derive(Clone, Debug)]
8#[non_exhaustive]
9pub struct Image {
10 pub guid: Option<String>,
13 pub visual_reference: Option<VisualReferenceImage>,
15 pub projection: Option<Projection>,
17 pub transform: Option<Transform>,
19 pub pointcloud_guid: Option<String>,
21 pub name: Option<String>,
23 pub description: Option<String>,
25 pub acquisition: Option<DateTime>,
27 pub sensor_vendor: Option<String>,
29 pub sensor_model: Option<String>,
31 pub sensor_serial: Option<String>,
33}
34
35impl Image {
36 fn from_node(node: &Node) -> Result<Self> {
37 let guid = xml::opt_string(node, "guid")?;
38 let pointcloud_guid = xml::opt_string(node, "associatedData3DGuid")?;
39 let transform = xml::opt_transform(node, "pose")?;
40 let name = xml::opt_string(node, "name")?;
41 let description = xml::opt_string(node, "description")?;
42 let sensor_model = xml::opt_string(node, "sensorModel")?;
43 let sensor_vendor = xml::opt_string(node, "sensorVendor")?;
44 let sensor_serial = xml::opt_string(node, "sensorSerialNumber")?;
45 let acquisition = xml::opt_date_time(node, "acquisitionDateTime")?;
46 let projection = Projection::from_image_node(node)?;
47
48 let visual_reference_node = node
49 .children()
50 .find(|n| n.has_tag_name("visualReferenceRepresentation"));
51 let visual_reference = if let Some(node) = visual_reference_node {
52 Some(VisualReferenceImage::from_node(&node)?)
53 } else {
54 None
55 };
56
57 Ok(Self {
58 guid,
59 pointcloud_guid,
60 transform,
61 name,
62 description,
63 acquisition,
64 sensor_vendor,
65 sensor_model,
66 sensor_serial,
67 projection,
68 visual_reference,
69 })
70 }
71
72 pub(crate) fn vec_from_document(document: &Document) -> Result<Vec<Self>> {
73 let mut images = Vec::new();
74 if let Some(images2d_node) = document.descendants().find(|n| n.has_tag_name("images2D")) {
75 for n in images2d_node.children() {
76 if n.has_tag_name("vectorChild") && n.attribute("type") == Some("Structure") {
77 let image = Self::from_node(&n)?;
78 images.push(image);
79 }
80 }
81 }
82 Ok(images)
83 }
84
85 pub(crate) fn xml_string(&self) -> String {
86 let mut xml = String::new();
87 xml += "<vectorChild type=\"Structure\">\n";
88 if let Some(guid) = &self.guid {
89 xml += &xml::gen_string("guid", &guid);
90 }
91 if let Some(vis_ref) = &self.visual_reference {
92 xml += &vis_ref.xml_string();
93 }
94 if let Some(rep) = &self.projection {
95 xml += &rep.xml_string();
96 }
97 if let Some(trans) = &self.transform {
98 xml += &trans.xml_string("pose");
99 }
100 if let Some(pc_guid) = &self.pointcloud_guid {
101 xml += &xml::gen_string("associatedData3DGuid", &pc_guid);
102 }
103 if let Some(name) = &self.name {
104 xml += &xml::gen_string("name", &name);
105 }
106 if let Some(desc) = &self.description {
107 xml += &xml::gen_string("description", &desc);
108 }
109 if let Some(acquisition) = &self.acquisition {
110 xml += &acquisition.xml_string("acquisitionDateTime");
111 }
112 if let Some(vendor) = &self.sensor_vendor {
113 xml += &xml::gen_string("sensorVendor", &vendor);
114 }
115 if let Some(model) = &self.sensor_model {
116 xml += &xml::gen_string("sensorModel", &model);
117 }
118 if let Some(serial) = &self.sensor_serial {
119 xml += &xml::gen_string("sensorSerialNumber", &serial);
120 }
121 xml += "</vectorChild>\n";
122 xml
123 }
124}
125
126#[derive(Debug, Clone)]
128pub enum Projection {
129 Pinhole(PinholeImage),
131 Spherical(SphericalImage),
133 Cylindrical(CylindricalImage),
135}
136
137impl Projection {
138 pub(crate) fn from_image_node(image_node: &Node) -> Result<Option<Self>> {
139 let pinhole = image_node
140 .children()
141 .find(|n| n.has_tag_name("pinholeRepresentation"));
142 if let Some(node) = &pinhole {
143 return Ok(Some(Self::Pinhole(PinholeImage::from_node(node)?)));
144 }
145
146 let spherical = image_node
147 .children()
148 .find(|n| n.has_tag_name("sphericalRepresentation"));
149 if let Some(node) = &spherical {
150 return Ok(Some(Self::Spherical(SphericalImage::from_node(node)?)));
151 }
152
153 let cylindrical = image_node
154 .children()
155 .find(|n| n.has_tag_name("cylindricalRepresentation"));
156 if let Some(node) = &cylindrical {
157 return Ok(Some(Self::Cylindrical(CylindricalImage::from_node(node)?)));
158 }
159
160 Ok(None)
161 }
162
163 pub(crate) fn xml_string(&self) -> String {
164 match self {
165 Projection::Pinhole(p) => p.xml_string(),
166 Projection::Spherical(s) => s.xml_string(),
167 Projection::Cylindrical(c) => c.xml_string(),
168 }
169 }
170}
171
172#[derive(Debug, Clone)]
174pub enum ImageFormat {
175 Png,
177 Jpeg,
179}
180
181#[derive(Debug, Clone)]
183#[non_exhaustive]
184pub struct ImageBlob {
185 pub data: Blob,
187 pub format: ImageFormat,
189}
190
191impl ImageBlob {
192 pub(crate) fn from_rep_node(rep_node: &Node) -> Result<Self> {
193 if let Some(node) = &rep_node.children().find(|n| n.has_tag_name("jpegImage")) {
194 Ok(Self {
195 data: Blob::from_node(node)?,
196 format: ImageFormat::Jpeg,
197 })
198 } else if let Some(node) = &rep_node.children().find(|n| n.has_tag_name("pngImage")) {
199 Ok(Self {
200 data: Blob::from_node(node)?,
201 format: ImageFormat::Png,
202 })
203 } else {
204 Error::invalid("Cannot find PNG or JPEG blob")
205 }
206 }
207
208 pub(crate) fn xml_string(&self) -> String {
209 match self.format {
210 ImageFormat::Png => self.data.xml_string("pngImage"),
211 ImageFormat::Jpeg => self.data.xml_string("jpegImage"),
212 }
213 }
214}
215
216#[derive(Clone, Debug)]
218pub struct VisualReferenceImageProperties {
219 pub width: u32,
221 pub height: u32,
223}
224
225#[derive(Clone, Debug)]
229#[non_exhaustive]
230pub struct VisualReferenceImage {
231 pub blob: ImageBlob,
233 pub properties: VisualReferenceImageProperties,
235 pub mask: Option<Blob>,
242}
243
244impl VisualReferenceImage {
245 pub(crate) fn from_node(node: &Node) -> Result<Self> {
246 Ok(Self {
247 blob: ImageBlob::from_rep_node(node)?,
248 mask: Blob::from_parent_node("imageMask", node)?,
249 properties: VisualReferenceImageProperties {
250 width: xml::req_int(node, "imageWidth")?,
251 height: xml::req_int(node, "imageHeight")?,
252 },
253 })
254 }
255
256 pub(crate) fn xml_string(&self) -> String {
257 let mut xml = String::new();
258 xml += "<visualReferenceRepresentation type=\"Structure\">\n";
259 xml += &self.blob.xml_string();
260 if let Some(mask) = &self.mask {
261 xml += &mask.xml_string("imageMask");
262 }
263 xml += &xml::gen_int("imageWidth", self.properties.width);
264 xml += &xml::gen_int("imageHeight", self.properties.height);
265 xml += "</visualReferenceRepresentation>\n";
266 xml
267 }
268}
269
270#[derive(Clone, Debug)]
272pub struct PinholeImageProperties {
273 pub width: u32,
275 pub height: u32,
277 pub focal_length: f64,
279 pub pixel_width: f64,
281 pub pixel_height: f64,
283 pub principal_x: f64,
285 pub principal_y: f64,
287}
288
289#[derive(Clone, Debug)]
291#[non_exhaustive]
292pub struct PinholeImage {
293 pub blob: ImageBlob,
295 pub properties: PinholeImageProperties,
297 pub mask: Option<Blob>,
304}
305
306impl PinholeImage {
307 pub(crate) fn from_node(node: &Node) -> Result<Self> {
308 Ok(Self {
309 blob: ImageBlob::from_rep_node(node)?,
310 mask: Blob::from_parent_node("imageMask", node)?,
311 properties: PinholeImageProperties {
312 width: xml::req_int(node, "imageWidth")?,
313 height: xml::req_int(node, "imageHeight")?,
314 focal_length: xml::req_f64(node, "focalLength")?,
315 pixel_width: xml::req_f64(node, "pixelWidth")?,
316 pixel_height: xml::req_f64(node, "pixelHeight")?,
317 principal_x: xml::req_f64(node, "principalPointX")?,
318 principal_y: xml::req_f64(node, "principalPointY")?,
319 },
320 })
321 }
322
323 pub(crate) fn xml_string(&self) -> String {
324 let mut xml = String::new();
325 xml += "<pinholeRepresentation type=\"Structure\">\n";
326 xml += &self.blob.xml_string();
327 if let Some(mask) = &self.mask {
328 xml += &mask.xml_string("imageMask");
329 }
330 xml += &xml::gen_int("imageWidth", self.properties.width);
331 xml += &xml::gen_int("imageHeight", self.properties.height);
332 xml += &xml::gen_float("focalLength", self.properties.focal_length);
333 xml += &xml::gen_float("pixelWidth", self.properties.pixel_width);
334 xml += &xml::gen_float("pixelHeight", self.properties.pixel_height);
335 xml += &xml::gen_float("principalPointX", self.properties.principal_x);
336 xml += &xml::gen_float("principalPointY", self.properties.principal_y);
337 xml += "</pinholeRepresentation>\n";
338 xml
339 }
340}
341
342#[derive(Clone, Debug)]
344pub struct SphericalImageProperties {
345 pub width: u32,
347 pub height: u32,
349 pub pixel_width: f64,
351 pub pixel_height: f64,
353}
354
355#[derive(Clone, Debug)]
357#[non_exhaustive]
358pub struct SphericalImage {
359 pub blob: ImageBlob,
361 pub properties: SphericalImageProperties,
363 pub mask: Option<Blob>,
370}
371
372impl SphericalImage {
373 pub(crate) fn from_node(node: &Node) -> Result<Self> {
374 let width = xml::req_int(node, "imageWidth")?;
375 let height = xml::req_int(node, "imageHeight")?;
376 Ok(Self {
377 blob: ImageBlob::from_rep_node(node)?,
378 mask: Blob::from_parent_node("imageMask", node)?,
379 properties: SphericalImageProperties {
380 width,
381 height,
382 pixel_width: xml::opt_f64(node, "pixelWidth")?.unwrap_or((2.0 * PI) / width as f64),
385 pixel_height: xml::opt_f64(node, "pixelHeight")?.unwrap_or(PI / height as f64),
386 },
387 })
388 }
389
390 pub(crate) fn xml_string(&self) -> String {
391 let mut xml = String::new();
392 xml += "<sphericalRepresentation type=\"Structure\">\n";
393 xml += &self.blob.xml_string();
394 if let Some(mask) = &self.mask {
395 xml += &mask.xml_string("imageMask");
396 }
397 xml += &xml::gen_int("imageWidth", self.properties.width);
398 xml += &xml::gen_int("imageHeight", self.properties.height);
399 xml += &xml::gen_float("pixelWidth", self.properties.pixel_width);
400 xml += &xml::gen_float("pixelHeight", self.properties.pixel_height);
401 xml += "</sphericalRepresentation>\n";
402 xml
403 }
404}
405
406#[derive(Clone, Debug)]
408pub struct CylindricalImageProperties {
409 pub width: u32,
411 pub height: u32,
413 pub radius: f64,
415 pub principal_y: f64,
417 pub pixel_width: f64,
419 pub pixel_height: f64,
421}
422
423#[derive(Clone, Debug)]
425#[non_exhaustive]
426pub struct CylindricalImage {
427 pub blob: ImageBlob,
429 pub properties: CylindricalImageProperties,
431 pub mask: Option<Blob>,
438}
439
440impl CylindricalImage {
441 pub(crate) fn from_node(node: &Node) -> Result<Self> {
442 Ok(Self {
443 blob: ImageBlob::from_rep_node(node)?,
444 mask: Blob::from_parent_node("imageMask", node)?,
445 properties: CylindricalImageProperties {
446 width: xml::req_int(node, "imageWidth")?,
447 height: xml::req_int(node, "imageHeight")?,
448 radius: xml::req_f64(node, "radius")?,
449 principal_y: xml::req_f64(node, "principalPointY")?,
450 pixel_width: xml::req_f64(node, "pixelWidth")?,
451 pixel_height: xml::req_f64(node, "pixelHeight")?,
452 },
453 })
454 }
455
456 pub(crate) fn xml_string(&self) -> String {
457 let mut xml = String::new();
458 xml += "<cylindricalRepresentation type=\"Structure\">\n";
459 xml += &self.blob.xml_string();
460 if let Some(mask) = &self.mask {
461 xml += &mask.xml_string("imageMask");
462 }
463 xml += &xml::gen_int("imageWidth", self.properties.width);
464 xml += &xml::gen_int("imageHeight", self.properties.height);
465 xml += &xml::gen_float("readius", self.properties.radius);
466 xml += &xml::gen_float("principalPointY", self.properties.principal_y);
467 xml += &xml::gen_float("pixelWidth", self.properties.pixel_width);
468 xml += &xml::gen_float("pixelHeight", self.properties.pixel_height);
469 xml += "</cylindricalRepresentation>\n";
470 xml
471 }
472}