tracklib/
polyline.rs

1use crate::simplify::Point;
2use crate::surface::{RoadClassId, SurfaceTypeId};
3use std::convert::TryFrom;
4
5#[derive(Debug)]
6pub enum PointField {
7    Y,
8    X,
9    D,
10    E,
11    S(SurfaceTypeId),
12    R(RoadClassId),
13}
14
15#[derive(Debug)]
16pub struct FieldEncodeOptions {
17    field: PointField,
18    factor: f64,
19}
20
21impl FieldEncodeOptions {
22    pub fn new(field: PointField, precision: u32) -> Self {
23        Self {
24            field,
25            factor: f64::from(10_u32.pow(precision)),
26        }
27    }
28}
29
30fn scale(n: f64, factor: f64) -> i64 {
31    (n * factor).round() as i64
32}
33
34fn encode(current: f64, previous: f64, factor: f64) -> String {
35    let current_scaled = scale(current, factor);
36    let previous_scaled = scale(previous, factor);
37    let diff = current_scaled - previous_scaled;
38    let mut v = diff << 1;
39    if diff < 0 {
40        v = !v;
41    }
42
43    let mut output = String::new();
44    while v >= 0x20 {
45        let from_char = char::from_u32(((0x20 | (v & 0x1f)) + 63) as u32).unwrap();
46        output.push(from_char);
47        v >>= 5;
48    }
49    let from_char = char::from_u32((v + 63) as u32).unwrap();
50    output.push(from_char);
51    output
52}
53
54pub(crate) fn polyline_encode(points: &[Point], fields: &[FieldEncodeOptions]) -> String {
55    let mut output = String::new();
56    let mut prev = &Point{index: 0,
57                          x: 0.0,
58                          y: 0.0,
59                          d: 0.0,
60                          e: 0.0,
61                          s: Some(0),
62                          r: Some(0)};
63
64    for point in points {
65        for field in fields {
66            match field.field {
67                PointField::Y => output.push_str(&encode(point.y, prev.y, field.factor)),
68                PointField::X => output.push_str(&encode(point.x, prev.x, field.factor)),
69                PointField::D => output.push_str(&encode(point.d, prev.d, field.factor)),
70                PointField::E => output.push_str(&encode(point.e, prev.e, field.factor)),
71                PointField::S(default_surface_id) => output.push_str(&encode(f64::from(i32::try_from(point.s.unwrap_or(default_surface_id)).unwrap_or(0)),
72                                                                             f64::from(i32::try_from(prev.s.unwrap_or(default_surface_id)).unwrap_or(0)),
73                                                                             field.factor)),
74                PointField::R(default_road_class_id) => output.push_str(&encode(f64::from(i32::try_from(point.r.unwrap_or(default_road_class_id)).unwrap_or(0)),
75                                                                                f64::from(i32::try_from(prev.r.unwrap_or(default_road_class_id)).unwrap_or(0)),
76                                                                                field.factor)),
77            }
78        }
79
80        prev = point;
81    }
82
83    output
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_polyline() {
92        let fields = vec![
93            FieldEncodeOptions::new(PointField::Y, 5),
94            FieldEncodeOptions::new(PointField::X, 5),
95        ];
96        assert_eq!(
97            polyline_encode(
98                &vec![
99                    Point {
100                        x: -120.200,
101                        y: 38.500,
102                        ..Default::default()
103                    },
104                    Point {
105                        x: -120.950,
106                        y: 40.700,
107                        ..Default::default()
108                    },
109                    Point {
110                        x: -126.453,
111                        y: 43.252,
112                        ..Default::default()
113                    }
114                ],
115                &fields
116            ),
117            "_p~iF~ps|U_ulLnnqC_mqNvxq`@".to_string()
118        );
119    }
120
121    #[test]
122    fn test_surface_encoding() {
123        let fields = vec![
124            FieldEncodeOptions::new(PointField::S(99), 5),
125            FieldEncodeOptions::new(PointField::R(10), 5),
126        ];
127        assert_eq!(
128            polyline_encode(
129                &vec![
130                    Point {
131                        s: None,
132                        r: Some(50),
133                        ..Default::default()
134                    },
135                    Point {
136                        s: Some(4),
137                        r: None,
138                        ..Default::default()
139                    },
140                ],
141                &fields
142            ),
143            "_}f{Q_sdpH~tybQ~ncsF".to_string()
144        );
145    }
146}