solverforge_maps/
geometry.rs1use serde::Serialize;
4use utoipa::ToSchema;
5
6use crate::routing::Coord;
7
8pub fn encode_polyline(coords: &[Coord]) -> String {
22 if coords.is_empty() {
23 return String::new();
24 }
25
26 let mut result = String::new();
27 let mut prev_lat = 0i64;
28 let mut prev_lng = 0i64;
29
30 for coord in coords {
31 let lat_e5 = (coord.lat * 1e5).round() as i64;
32 let lng_e5 = (coord.lng * 1e5).round() as i64;
33
34 encode_value(lat_e5 - prev_lat, &mut result);
35 encode_value(lng_e5 - prev_lng, &mut result);
36
37 prev_lat = lat_e5;
38 prev_lng = lng_e5;
39 }
40
41 result
42}
43
44fn encode_value(value: i64, output: &mut String) {
45 let mut encoded = if value < 0 {
46 !((value) << 1)
47 } else {
48 (value) << 1
49 };
50
51 while encoded >= 0x20 {
52 output.push(char::from_u32(((encoded & 0x1f) | 0x20) as u32 + 63).unwrap());
53 encoded >>= 5;
54 }
55 output.push(char::from_u32(encoded as u32 + 63).unwrap());
56}
57
58pub fn decode_polyline(encoded: &str) -> Vec<Coord> {
72 let mut coords = Vec::new();
73 let mut lat = 0i64;
74 let mut lng = 0i64;
75 let bytes = encoded.as_bytes();
76 let mut i = 0;
77
78 while i < bytes.len() {
79 let (lat_delta, consumed) = decode_value(&bytes[i..]);
80 i += consumed;
81 lat += lat_delta;
82
83 if i >= bytes.len() {
84 break;
85 }
86
87 let (lng_delta, consumed) = decode_value(&bytes[i..]);
88 i += consumed;
89 lng += lng_delta;
90
91 coords.push(Coord::new(lat as f64 / 1e5, lng as f64 / 1e5));
92 }
93
94 coords
95}
96
97fn decode_value(bytes: &[u8]) -> (i64, usize) {
98 let mut result = 0i64;
99 let mut shift = 0;
100 let mut consumed = 0;
101
102 for &b in bytes {
103 consumed += 1;
104 let chunk = (b as i64) - 63;
105 result |= (chunk & 0x1f) << shift;
106 shift += 5;
107
108 if chunk < 0x20 {
109 break;
110 }
111 }
112
113 if result & 1 != 0 {
114 result = !(result >> 1);
115 } else {
116 result >>= 1;
117 }
118
119 (result, consumed)
120}
121
122#[derive(Debug, Clone, Serialize, ToSchema)]
123pub struct EncodedSegment {
124 pub entity_idx: usize,
125 pub entity_name: String,
126 pub polyline: String,
127 pub point_count: usize,
128}
129
130impl EncodedSegment {
131 pub fn new(entity_idx: usize, entity_name: impl Into<String>, coords: &[Coord]) -> Self {
132 Self {
133 entity_idx,
134 entity_name: entity_name.into(),
135 polyline: encode_polyline(coords),
136 point_count: coords.len(),
137 }
138 }
139}