1pub fn decode(encoded_path: &str, precision: i32) -> Vec<[f64; 2]> {
36 let factor = 10f64.powi(precision);
37 let len = encoded_path.len();
38 let mut path = Vec::with_capacity(len / 2);
39 let mut index = 0;
40 let mut lat = 0;
41 let mut lng = 0;
42
43 while index < len {
44 let (result, new_index) = decode_unsigned(encoded_path.as_bytes(), index);
45 index = new_index;
46 lat += if result & 1 != 0 {
47 !(result >> 1)
48 } else {
49 result >> 1
50 };
51
52 let (result, new_index) = decode_unsigned(encoded_path.as_bytes(), index);
53 index = new_index;
54 lng += if result & 1 != 0 {
55 !(result >> 1)
56 } else {
57 result >> 1
58 };
59
60 path.push([lat as f64 / factor, lng as f64 / factor]);
61 }
62
63 path
64}
65
66pub fn encode(path: &[[f64; 2]], precision: i32) -> Vec<u8> {
81 let factor = 10f64.powi(precision);
82 let transform = |point: &[f64; 2]| -> [i32; 2] {
83 [
84 (point[0] * factor).round() as i32,
85 (point[1] * factor).round() as i32,
86 ]
87 };
88
89 polyline_encode_line(path, transform)
90}
91
92#[inline(always)]
93fn decode_unsigned(encoded: &[u8], mut index: usize) -> (i32, usize) {
94 let mut result = 1;
95 let mut shift = 0;
96
97 while let Some(&byte) = encoded.get(index) {
98 let b = (byte as i32) - 63 - 1;
99 index += 1;
100 result += b << shift;
101 shift += 5;
102 if b < 0x1f {
103 break;
104 }
105 }
106
107 (result, index)
108}
109
110fn polyline_encode_line<F>(path: &[[f64; 2]], transform: F) -> Vec<u8>
111where
112 F: Fn(&[f64; 2]) -> [i32; 2],
113{
114 let mut result = Vec::with_capacity(path.len() * 4);
116 let mut start = [0, 0];
117
118 for point in path {
119 let end = transform(point);
120 polyline_encode_signed(end[0] - start[0], &mut result);
121 polyline_encode_signed(end[1] - start[1], &mut result);
122 start = end;
123 }
124
125 result
126}
127
128fn polyline_encode_signed(value: i32, result: &mut Vec<u8>) {
129 polyline_encode_unsigned(if value < 0 { !(value << 1) } else { value << 1 }, result);
130}
131
132fn polyline_encode_unsigned(mut value: i32, result: &mut Vec<u8>) {
133 while value >= 0x20 {
134 result.push(((0x20 | (value & 0x1f)) + 63) as u8);
135 value >>= 5;
136 }
137 result.push((value + 63) as u8);
138}
139
140#[cfg(test)]
141mod tests {
142 use core::str;
143
144 use super::*;
145
146 const DEFAULT: [[f64; 2]; 3] = [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]];
148
149 const DEFAULT_ROUNDED: [[f64; 2]; 3] = [[39.0, -120.0], [41.0, -121.0], [43.0, -126.0]];
150
151 const SLASHES: [[f64; 2]; 3] = [[35.6, -82.55], [35.59985, -82.55015], [35.6, -82.55]];
152
153 const ROUNDING: [[f64; 2]; 2] = [[0.0, 0.000006], [0.0, 0.000002]];
154
155 const NEGATIVE: [[f64; 2]; 3] = [
156 [36.05322, -112.084004],
157 [36.053573, -112.083914],
158 [36.053845, -112.083965],
159 ];
160
161 #[test]
162 fn decode_empty() {
163 assert!(decode("", 5).is_empty());
164 }
165
166 #[test]
167 fn decode_default() {
168 let decoded = decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@", 5);
169 for (i, point) in DEFAULT.iter().enumerate() {
170 assert!((decoded[i][0] - point[0]).abs() < 1e-5);
171 assert!((decoded[i][1] - point[1]).abs() < 1e-5);
172 }
173 }
174
175 #[test]
176 fn decode_custom_precision() {
177 let decoded = decode("_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI", 6);
178 for (i, point) in DEFAULT.iter().enumerate() {
179 assert!((decoded[i][0] - point[0]).abs() < 1e-6);
180 assert!((decoded[i][1] - point[1]).abs() < 1e-6);
181 }
182 }
183
184 #[test]
185 fn decode_precision_zero() {
186 let decoded = decode("mAnFC@CH", 0);
187 for (i, point) in DEFAULT_ROUNDED.iter().enumerate() {
188 assert!((decoded[i][0] - point[0]).abs() < 1.0);
189 assert!((decoded[i][1] - point[1]).abs() < 1.0);
190 }
191 }
192
193 #[test]
194 fn roundtrip() {
195 let encoded = "gcneIpgxzRcDnBoBlEHzKjBbHlG`@`IkDxIiKhKoMaLwTwHeIqHuAyGXeB~Ew@fFjAtIzExF";
196 let decoded = decode(encoded, 5);
197 assert_eq!(str::from_utf8(&encode(&decoded, 5)).unwrap(), encoded);
198 }
199
200 #[test]
201 fn roundtrip_slashes() {
202 let encoded = encode(&SLASHES, 5);
203 let decoded = decode(str::from_utf8(&encoded).unwrap(), 5);
204 for (i, point) in SLASHES.iter().enumerate() {
205 assert!((decoded[i][0] - point[0]).abs() < 1e-5);
206 assert!((decoded[i][1] - point[1]).abs() < 1e-5);
207 }
208 }
209
210 #[test]
211 fn encode_empty() {
212 assert_eq!(str::from_utf8(&encode(&[], 5)).unwrap(), "");
213 }
214
215 #[test]
216 fn encode_default() {
217 assert_eq!(
218 str::from_utf8(&encode(&DEFAULT, 5)).unwrap(),
219 "_p~iF~ps|U_ulLnnqC_mqNvxq`@"
220 );
221 }
222
223 #[test]
224 fn encode_rounding() {
225 assert_eq!(str::from_utf8(&encode(&ROUNDING, 5)).unwrap(), "?A?@");
226 }
227
228 #[test]
229 fn encode_negative() {
230 assert_eq!(
231 str::from_utf8(&encode(&NEGATIVE, 5)).unwrap(),
232 "ss`{E~kbkTeAQw@J"
233 );
234 }
235
236 #[test]
237 fn encode_custom_precision() {
238 assert_eq!(
239 str::from_utf8(&encode(&DEFAULT, 6)).unwrap(),
240 "_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI"
241 );
242 }
243
244 #[test]
245 fn encode_precision_zero() {
246 assert_eq!(str::from_utf8(&encode(&DEFAULT, 0)).unwrap(), "mAnFC@CH");
247 }
248
249 #[test]
250 fn encode_negative_values() {
251 let point = [[-107.3741825, 0.0]];
252 let encoded = encode(&point, 7);
253 let decoded = decode(str::from_utf8(&encoded).unwrap(), 7);
254 assert!(decoded[0][0] < 0.0);
255 }
256
257 #[test]
258 fn encode_decode() {
259 let points = vec![[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]];
260 let encoded = encode(&points, 5);
261 assert_eq!(
262 str::from_utf8(&encoded).unwrap(),
263 "_p~iF~ps|U_ulLnnqC_mqNvxq`@"
264 );
265
266 let decoded = decode(str::from_utf8(&encoded).unwrap(), 5);
267 for (i, point) in points.iter().enumerate() {
268 assert!((decoded[i][0] - point[0]).abs() < 1e-10);
269 assert!((decoded[i][1] - point[1]).abs() < 1e-10);
270 }
271 }
272}