polyline_iter/
lib.rs

1/// Iterator over geographic coordinates (latitude/longitude pairs) decoded from a polyline-encoded string.
2///
3/// Supports both formats:
4/// - polyline5: Google Maps standard format with 5 decimal places precision
5/// - polyline6: Higher precision format with 6 decimal places, used by routing engines like OSRM and Valhalla
6///
7/// For details on the encoding algorithm, see:
8/// https://developers.google.com/maps/documentation/utilities/polylinealgorithm
9///
10/// # Examples
11///
12/// ```
13/// let iter = polyline_iter::decode(6, "avs_iB}xlxWissBw|zEu``AsxgCyoaAm_z@");
14/// assert_eq!(
15///     iter.collect::<Vec<_>>(),
16///     vec![
17///         (55.585137, 12.999583),
18///         (55.644854, 13.112187),
19///         (55.678161, 13.182229),
20///         (55.712222, 13.212444),
21///     ]
22/// );
23///
24/// // Count points without collecting them
25/// assert_eq!(polyline_iter::decode(5, "avs_iB}xlxWissBw|zEu``AsxgCyoaAm_z@").count(), 4);
26///
27/// // Iterator approach allows to transcode polyline to another precision without intermediate allocations.
28/// let polyline5 = polyline_iter::encode(5, polyline_iter::decode(6, "avs_iB}xlxWissBw|zEu``AsxgCyoaAm_z@"));
29/// assert_eq!(polyline5, "cngrIk~inAgtJw~TeoEwtL{sE{{D");
30/// assert_eq!(
31///     polyline_iter::decode(5, &polyline5).collect::<Vec<_>>(),
32///     vec![
33///         (55.58514, 12.99958),
34///         (55.64486, 13.11218),
35///         (55.67817, 13.18222),
36///         (55.71223, 13.21244)
37///     ],
38/// );
39/// ```
40pub struct PolylineIter<'a> {
41    polyline: &'a [u8],
42    scale: f64,
43    /// Last processed latitude, multiplied by the scale.
44    lat: i32,
45    /// Last processed longitude, multiplied by the scale.
46    lon: i32,
47}
48
49impl<'a> PolylineIter<'a> {
50    /// Creates a new iterator over points decoded from a polyline.
51    /// The precision is the number of decimal places in the coordinates, which is 5 for polyline5 and 6 for polyline6.
52    #[inline(always)]
53    pub fn new(precision: u8, polyline: &'a str) -> Self {
54        assert!(precision <= 7, "i32 can hold up to 180 * 10^7");
55        PolylineIter {
56            polyline: polyline.as_bytes(),
57            lat: 0,
58            lon: 0,
59            scale: 10.0_f64.powi(precision as i32),
60        }
61    }
62
63    #[inline(always)]
64    fn varint_decode(&mut self) -> Option<u32> {
65        let mut result = 0;
66        for i in 0..self.polyline.len().min(7) {
67            // Casting here to i32 here to provide bad value instead of overflow panicking on bad input.
68            let chunk = (self.polyline[i] as i32) - 63;
69            result |= (chunk & 0x1f) << (i * 5); // no overflow as i <= 6
70            if chunk & 0x20 == 0 {
71                self.polyline = &self.polyline[i + 1..];
72                return Some(result as u32);
73            }
74        }
75        None
76    }
77
78    /// O(n) operation to count the number of points in the polyline without consuming the iterator.
79    pub fn len(&self) -> usize {
80        self.polyline
81            .iter()
82            .filter(|&&byte| (byte as i8 - 63) & 0x20 == 0)
83            .count()
84            / 2 // Each point has 2 numbers
85    }
86
87    /// Checks if the polyline contains no points.
88    pub fn is_empty(&self) -> bool {
89        self.polyline
90            .iter()
91            .filter(|&&byte| (byte as i8 - 63) & 0x20 == 0)
92            .nth(1)
93            .is_none()
94    }
95}
96
97impl Iterator for PolylineIter<'_> {
98    type Item = (f64, f64);
99
100    fn next(&mut self) -> Option<Self::Item> {
101        let lat_change = self.varint_decode()?;
102        let lon_change = self.varint_decode()?;
103        self.lat += zigzag_decode(lat_change);
104        self.lon += zigzag_decode(lon_change);
105        let lat = self.lat as f64 / self.scale;
106        let lon = self.lon as f64 / self.scale;
107        Some((lat, lon))
108    }
109
110    fn size_hint(&self) -> (usize, Option<usize>) {
111        // There are at least polyline.len() / 12 points as each i32 is encoded in 5 bits per char.
112        // And at most polyline.len() / 2 points if each number (2 per point) is encoded only by a single char.
113        let len = self.polyline.len();
114        (len / 12, Some(len / 2))
115    }
116
117    fn count(self) -> usize {
118        self.len()
119    }
120}
121
122/// Decodes a polyline-encoded string into an iterator over geographic coordinates (latitude/longitude pairs).
123///
124/// This is a convenience function that wraps [`PolylineIter::new()`] and returns an iterator over points.
125/// The precision parameter specifies the number of decimal places in the coordinates (5 for polyline5,
126/// 6 for polyline6), with a maximum value of 7 which corresponds to ~1cm precision at the equator.
127///
128/// ```
129/// use polyline_iter::decode;
130///
131/// // Decode a polyline5 string (Google Maps standard format)
132/// let points: Vec<_> = decode(5, "angrIk~inAgwDybH").collect();
133/// assert_eq!(points, vec![(55.58513, 12.99958), (55.61461, 13.04627)]);
134///
135/// // Decode a polyline6 string (higher precision format)
136/// let points: Vec<_> = decode(6, "avs_iB}xlxWissBw|zEu``AsxgCyoaAm_z@").collect();
137/// assert_eq!(
138///     points,
139///     vec![
140///         (55.585137, 12.999583),
141///         (55.644854, 13.112187),
142///         (55.678161, 13.182229),
143///         (55.712222, 13.212444),
144///     ]
145/// );
146///
147/// // Count points without collecting them
148/// assert_eq!(decode(5, "angrIk~inAgwDybH").count(), 2);
149/// ```
150#[inline(always)]
151pub fn decode(precision: u8, polyline: &str) -> PolylineIter<'_> {
152    PolylineIter::new(precision, polyline)
153}
154
155/// Encodes a sequence of points (latitude, longitude pairs) into a polyline string with the given precision.
156/// The precision parameter specifies the number of decimal places in the coordinates (5 for polyline5,
157/// 6 for polyline6), with a maximum value of 7 which corresponds to ~1cm precision at the equator.
158///
159/// ```
160/// // Encode an array of latitude/longitude coordinates with precision 5 (standard for Google Maps)
161/// assert_eq!(polyline_iter::encode(5, [(55.58513, 12.99958), (55.61461, 13.04627)]),"angrIk~inAgwDybH");
162///
163/// // `encode()` accepts any iterator that produce (lat,lon)
164/// let iter = (1..5).map(|i| (55.5 + 0.1 * i as f64, 12.9 + 0.1 * i as f64));
165/// assert_eq!(polyline_iter::encode(5, iter), "_kjrI_ajnA_pR_pR_pR_pR_pR_pR");
166///
167/// // And it can be used with slices as well (convert iter of references to iter of values with `copied()`)
168/// let points = vec![
169///     (55.58513, 12.99958),
170///     (55.61461, 13.04627),
171///     (55.64485, 13.11219),
172///     (55.67816, 13.18223),
173///     (55.71840, 13.22343),
174/// ];
175/// assert_eq!(polyline_iter::encode(5, points[1..3].iter().copied()), "ifmrIebsnA_|D_{K");
176/// ```
177pub fn encode(precision: u8, points: impl IntoIterator<Item = (f64, f64)>) -> String {
178    assert!(precision <= 7, "i32 can hold up to 180 * 10^7");
179
180    let scale = 10.0_f64.powi(precision as i32);
181    let mut result = String::with_capacity(16);
182
183    let mut prev = (0.0, 0.0);
184    for point in points {
185        let lat_change = ((point.0 - prev.0) * scale).round() as i32;
186        let lon_change = ((point.1 - prev.1) * scale).round() as i32;
187
188        varint_encode(zigzag_encode(lat_change), &mut result);
189        varint_encode(zigzag_encode(lon_change), &mut result);
190
191        prev = point;
192    }
193    result
194}
195
196/// Zigzag encoded numbers store the sign in the least significant bit, which this function moves to the sign bit.
197fn zigzag_decode(i: u32) -> i32 {
198    (i >> 1) as i32 ^ -((i & 1) as i32)
199}
200
201/// Moves the sign bit from the most significant bit to the least significant bit,
202/// thus reducing number of significant bits for negative numbers.
203fn zigzag_encode(value: i32) -> u32 {
204    (value << 1) as u32 ^ (value >> 31) as u32
205}
206
207/// Encodes the value into a variable-length format, storing 5 bits per byte to keep
208/// all bytes URL-compatible (from 63 to 126).
209fn varint_encode(mut value: u32, buffer: &mut String) {
210    while value >= 0x20 {
211        let byte = char::from_u32(((value & 0x1F) | 0x20) + 63).unwrap();
212        buffer.push(byte);
213        value >>= 5;
214    }
215    let byte = char::from_u32(value + 63).unwrap();
216    buffer.push(byte);
217}
218
219#[cfg(test)]
220mod tests {
221    use super::*;
222    use pretty_assertions::assert_eq;
223
224    /// Checks if the polyline contains only valid characters and ends with a complete point.
225    fn check_polyline(polyline: &str) -> bool {
226        let bytes = polyline.as_bytes();
227        let mut stop_bytes = 0;
228        for &byte in bytes {
229            if (byte as u32) < 63 || (byte as u32) > 127 {
230                return false;
231            }
232            if (byte - 63) & 0x20 == 0 {
233                stop_bytes += 1;
234            }
235        }
236        // The polyline is valid if it ends with a complete point.
237        stop_bytes % 2 == 0 && bytes.last().map(|b| b & 0x20 == 0).unwrap_or(true)
238    }
239
240    #[test]
241    fn zigzag() {
242        assert_eq!(zigzag_decode(0), 0);
243        assert_eq!(zigzag_decode(1), -1);
244        assert_eq!(zigzag_decode(2), 1);
245        assert_eq!(zigzag_decode(3), -2);
246        assert_eq!(zigzag_decode(4), 2);
247        assert_eq!(zigzag_decode(5), -3);
248        assert_eq!(zigzag_decode(6), 3);
249        assert_eq!(zigzag_decode(7), -4);
250        assert_eq!(zigzag_decode(8), 4);
251        assert_eq!(zigzag_decode(9), -5);
252        assert_eq!(zigzag_decode(10), 5);
253        assert_eq!(zigzag_decode(11), -6);
254        assert_eq!(zigzag_decode(12), 6);
255        assert_eq!(zigzag_decode(13), -7);
256        assert_eq!(zigzag_decode(14), 7);
257        assert_eq!(zigzag_decode(15), -8);
258
259        assert_eq!(zigzag_encode(0), 0);
260        assert_eq!(zigzag_encode(-1), 1);
261        assert_eq!(zigzag_encode(1), 2);
262        assert_eq!(zigzag_encode(-2), 3);
263        assert_eq!(zigzag_encode(2), 4);
264        assert_eq!(zigzag_encode(-3), 5);
265        assert_eq!(zigzag_encode(3), 6);
266        assert_eq!(zigzag_encode(-4), 7);
267        assert_eq!(zigzag_encode(4), 8);
268        assert_eq!(zigzag_encode(-5), 9);
269        assert_eq!(zigzag_encode(5), 10);
270        assert_eq!(zigzag_encode(-6), 11);
271        assert_eq!(zigzag_encode(6), 12);
272        assert_eq!(zigzag_encode(-7), 13);
273        assert_eq!(zigzag_encode(7), 14);
274        assert_eq!(zigzag_encode(-8), 15);
275    }
276
277    #[test]
278    fn empty_polyline() {
279        assert_eq!(decode(5, "").next(), None);
280        assert_eq!(encode(5, []), "");
281
282        assert_eq!(decode(6, "").next(), None);
283        assert_eq!(encode(6, []), "");
284    }
285
286    #[test]
287    fn single_point() {
288        assert_eq!(encode(5, [(0.0, 0.0)]), "??");
289        assert_eq!(decode(5, "??").collect::<Vec<_>>(), [(0.0, 0.0)]);
290        assert_eq!(encode(6, [(0.0, 0.0)]), "??");
291        assert_eq!(decode(6, "??").collect::<Vec<_>>(), [(0.0, 0.0)]);
292
293        let point = (55.71218211778275, 13.21561509233427);
294        assert_eq!(encode(5, [point]), "ch`sIsdtoA");
295        assert_eq!(
296            decode(5, "ch`sIsdtoA").collect::<Vec<_>>(),
297            [(55.71218, 13.21562)]
298        );
299        assert_eq!(encode(6, [point]), "kzkgiB}vreX");
300        assert_eq!(
301            decode(6, "kzkgiB}vreX").collect::<Vec<_>>(),
302            [(55.712182, 13.215615)]
303        );
304        assert_eq!(encode(7, [point]), "yp_se`@mnda{F");
305        assert_eq!(
306            decode(7, "yp_se`@mnda{F").collect::<Vec<_>>(),
307            [(55.7121821, 13.2156151)]
308        );
309
310        let point = (37.82070486887192, -122.47866012130189);
311        assert_eq!(encode(5, [point]), "kzyeFrrpjV");
312        assert_eq!(
313            decode(5, "kzyeFrrpjV").collect::<Vec<_>>(),
314            [(37.82070, -122.47866)]
315        );
316        assert_eq!(encode(6, [point]), "aqkcgAfcorhF");
317        assert_eq!(
318            decode(6, "aqkcgAfcorhF").collect::<Vec<_>>(),
319            [(37.820705, -122.478660)]
320        );
321
322        let point = (-54.906532713928094, -65.99208264367125);
323        assert_eq!(encode(5, [point]), "x|bnInaxqK");
324        assert_eq!(
325            decode(5, "x|bnInaxqK").collect::<Vec<_>>(),
326            [(-54.90653, -65.99208)]
327        );
328        assert_eq!(encode(6, [point]), "hifvgBdxyz|B");
329        assert_eq!(
330            decode(6, "hifvgBdxyz|B").collect::<Vec<_>>(),
331            [(-54.906533, -65.992083)]
332        );
333
334        let point = (-37.88209074375984, 144.79631245265494);
335        assert_eq!(encode(5, [point]), "`zefF}owrZ");
336        assert_eq!(
337            decode(5, "`zefF}owrZ").collect::<Vec<_>>(),
338            [(-37.88209, 144.79631)]
339        );
340        assert_eq!(encode(6, [point]), "tmcggAohtdsG");
341        assert_eq!(
342            decode(6, "tmcggAohtdsG").collect::<Vec<_>>(),
343            [(-37.882091, 144.796312)]
344        );
345    }
346
347    #[test]
348    fn multiple_points() {
349        let polyline = "angrIk~inAgwDybH_|D_{KeoEwtLozFo`Gre@tcA";
350        assert!(check_polyline(polyline));
351        assert_eq!(decode(5, polyline).count(), 6);
352
353        let mut iter = decode(5, polyline);
354        assert_eq!(iter.next(), Some((55.58513, 12.99958)));
355        assert_eq!(iter.next(), Some((55.61461, 13.04627)));
356        assert_eq!(iter.next(), Some((55.64485, 13.11219)));
357        assert_eq!(iter.next(), Some((55.67816, 13.18223)));
358        assert_eq!(iter.next(), Some((55.71840, 13.22343)));
359        assert_eq!(iter.next(), Some((55.71222, 13.21244)));
360        assert_eq!(iter.next(), None);
361
362        // If polyline is decoded with wrong precision, the points will be x10 times smaller or bigger.
363        let mut iter = decode(6, polyline);
364        assert_eq!(iter.next(), Some((5.558513, 1.299958)));
365        assert_eq!(iter.next(), Some((5.561461, 1.304627)));
366        assert_eq!(iter.next(), Some((5.564485, 1.311219)));
367        assert_eq!(iter.next(), Some((5.567816, 1.318223)));
368        assert_eq!(iter.next(), Some((5.571840, 1.322343)));
369        assert_eq!(iter.next(), Some((5.571222, 1.321244)));
370        assert_eq!(iter.next(), None);
371
372        // Forward and backward transcoding should give the same result
373        let polyline = "gzkgiBgwreX{@sI~HcBwBoi@sXvBsIcBgJSwGg@wGg@cG{@{JoAwGSkC{@ce@gOwj@oKsb@cBoFz@gEjC?~RRb[f@v[Sz@kHnAoA_l@SsIR?";
374        assert_eq!(encode(6, decode(6, polyline)), polyline);
375
376        // Transcoding polyline to another precision
377        assert_eq!(
378            encode(5, decode(6, polyline)),
379            "ch`sIsdtoAEa@^IKgCqAJa@Ic@A[C[CYEe@G[AMEyBs@kCg@qBIWDSL?~@@xABzAAD]FGoCAa@@?"
380        );
381
382        assert_eq!(
383            encode(
384                6,
385                // decoded with wrong precision, but then corrected by `* 10.0`
386                decode(7, polyline).map(|(lat, lon)| (lat * 10.0, lon * 10.0))
387            ),
388            polyline
389        );
390    }
391
392    #[test]
393    #[should_panic]
394    fn decode_bad_precision() {
395        decode(8, "");
396    }
397
398    #[test]
399    #[should_panic]
400    fn encode_bad_precision() {
401        encode(8, []);
402    }
403
404    #[test]
405    fn broken_string() {
406        // incomplete point
407        assert_eq!(decode(5, "?").next(), None);
408        assert_eq!(decode(5, "?").is_empty(), true);
409        assert_eq!(decode(5, "?").len(), 0);
410
411        // Last point is missing a lon change, so the whole points will be skipped.
412        let polyline = "_p~iF~ps|U_ulLnnqC_mqNvxq";
413        assert!(!check_polyline(polyline)); // the polyline is not valid, but still can be decoded.
414        let mut iter = decode(5, polyline);
415        assert_eq!(iter.len(), 2);
416        assert_eq!(iter.is_empty(), false);
417        assert_eq!(iter.next(), Some((38.5, -120.2)));
418        assert_eq!(iter.len(), 1);
419        assert_eq!(iter.is_empty(), false);
420        assert_eq!(iter.next(), Some((40.7, -120.95)));
421        assert_eq!(iter.len(), 0);
422        assert_eq!(iter.is_empty(), true);
423        assert_eq!(iter.next(), None);
424
425        let mut iter = decode(6, polyline);
426        assert_eq!(iter.len(), 2);
427        assert_eq!(iter.is_empty(), false);
428        assert_eq!(iter.next(), Some((3.85, -12.02)));
429        assert_eq!(iter.len(), 1);
430        assert_eq!(iter.is_empty(), false);
431        assert_eq!(iter.next(), Some((4.07, -12.095)));
432        assert_eq!(iter.len(), 0);
433        assert_eq!(iter.is_empty(), true);
434        assert_eq!(iter.next(), None);
435        assert_eq!(iter.len(), 0);
436        assert_eq!(iter.is_empty(), true);
437
438        assert_eq!(iter.next(), None); // just to make sure it does not panic
439        assert_eq!(iter.is_empty(), true);
440    }
441
442    #[test]
443    fn invalid_symbols() {
444        // `!` (33) is not a valid symbol for a polyline because it is not in the range [63, 127].
445        let polyline = "!!!!";
446        assert!(!check_polyline(polyline));
447        let mut iter = decode(5, polyline);
448        assert_eq!(iter.next(), None);
449
450        // Now let's add `!` in the middle of a valid polyline.
451        let polyline = "_p~iF~ps|U_ulLnnqC!_mqNvxq";
452        assert!(!check_polyline(polyline)); // the polyline is not valid, but still can be decoded.
453        let mut iter = decode(5, polyline);
454        assert_eq!(iter.next(), Some((38.5, -120.2)));
455        assert_eq!(iter.next(), Some((40.7, -120.95)));
456        assert_eq!(iter.next(), None);
457    }
458
459    #[test]
460    fn size_hint() {
461        let iter = decode(5, "_p~iF~ps|U_ulLnnqC_mqNvxq`@");
462        // Size hint should not be precise as the number of points depends the distance between them.
463        assert!(iter.size_hint().0 <= 3);
464        assert!(iter.size_hint().1.unwrap() >= 3);
465        assert_eq!(iter.count(), 3);
466    }
467}