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}