1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
use crate::error::Error;
use crate::error::Error::{MissingElements, Only3DSupported};
use egml_core::DirectPosition;
use quick_xml::de;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename = "gml:Point")]
struct GmlPoint {
    #[serde(rename = "@id", default)]
    id: String,
    pos: GmlPos,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename = "gml:pos")]
struct GmlPos {
    #[serde(rename = "@srsDimension")]
    srs_dimension: Option<u32>,
    #[serde(rename = "$value")]
    value: String,
}

impl GmlPos {
    pub fn to_direct_position(self) -> Result<DirectPosition, Error> {
        if self.srs_dimension.unwrap_or(3) != 3 {
            return Err(Only3DSupported());
        }

        let parsed_values: Vec<f64> = self
            .value
            .split_whitespace()
            .map(|s| s.parse().unwrap())
            .collect();
        if parsed_values.len() % 3 != 0 {
            return Err(MissingElements());
        }

        let position = DirectPosition::new(parsed_values[0], parsed_values[1], parsed_values[2])?;
        Ok(position)
    }
}

pub fn parse_point(source_text: &str) -> Result<DirectPosition, Error> {
    let parsed_point: GmlPoint = de::from_str(source_text)?;

    let point: DirectPosition = parsed_point.pos.to_direct_position()?;
    Ok(point)
}

#[cfg(test)]
mod tests {
    use crate::parse_point;

    #[test]
    fn parsing_point() {
        let source_text = "<gml:Point>
              <gml:pos srsDimension=\"3\" gml:id=\"UUID_6b33ecfa-6e08-4e8e-a4b5-e1d06540faf0\">678000.9484065345 5403659.060043676 417.3802376791456</gml:pos>
            </gml:Point>";

        let _result = parse_point(source_text).unwrap();
    }

    #[test]
    fn parsing_point_without_id() {
        let source_text = "<gml:Point>
              <gml:pos srsDimension=\"3\">678000.9484065345 5403659.060043676 417.3802376791456</gml:pos>
            </gml:Point>";

        let _result = parse_point(source_text).unwrap();
    }
}