///|
pub fn encode_polyline(
points : ArrayView[Point],
precision? : Int = 5,
) -> Result[String, RouteError] {
match validate_precision(precision) {
Ok(_) => ()
Err(err) => return Err(err)
}
match validate_points(points) {
Ok(_) => ()
Err(err) => return Err(err)
}
let scale = pow10_int(precision).to_double()
let out = StringBuilder(size_hint=points.length() * 10)
let mut prev_lat = 0
let mut prev_lon = 0
for point in points {
let lat = (point.lat * scale).round().to_int()
let lon = (point.lon * scale).round().to_int()
write_polyline_value(out, lat - prev_lat)
write_polyline_value(out, lon - prev_lon)
prev_lat = lat
prev_lon = lon
}
Ok(out.to_string())
}
///|
pub fn decode_polyline(
encoded : String,
precision? : Int = 5,
) -> Result[Array[Point], RouteError] {
match validate_precision(precision) {
Ok(_) => ()
Err(err) => return Err(err)
}
let scale = pow10_int(precision).to_double()
let points = Array::new()
let mut index = 0
let mut lat = 0
let mut lon = 0
while index < encoded.length() {
let (delta_lat, next_index) = match read_polyline_value(encoded, index) {
Ok(pair) => pair
Err(err) => return Err(err)
}
index = next_index
if index >= encoded.length() {
return Err(MalformedPolyline(pos=index, reason="missing longitude delta"))
}
let (delta_lon, next_index) = match read_polyline_value(encoded, index) {
Ok(pair) => pair
Err(err) => return Err(err)
}
index = next_index
lat = lat + delta_lat
lon = lon + delta_lon
let point = Point(lat.to_double() / scale, lon.to_double() / scale)
match validate_point(point, points.length()) {
Ok(_) => points.push(point)
Err(err) => return Err(err)
}
}
Ok(points)
}
///|
pub fn encode_polyline5(
points : ArrayView[Point],
) -> Result[String, RouteError] {
encode_polyline(points, precision=5)
}
///|
pub fn decode_polyline5(encoded : String) -> Result[Array[Point], RouteError] {
decode_polyline(encoded, precision=5)
}
///|
fn write_polyline_value(out : StringBuilder, delta : Int) -> Unit {
let mut value = delta * 2
if delta < 0 {
value = -value - 1
}
while value >= 0x20 {
out.write_char((0x20 + value % 0x20 + 63).unsafe_to_char())
value = value / 0x20
}
out.write_char((value + 63).unsafe_to_char())
}
///|
fn read_polyline_value(
encoded : String,
start : Int,
) -> Result[(Int, Int), RouteError] {
let mut index = start
let mut factor = 1
let mut result = 0
let mut groups = 0
while index < encoded.length() {
if groups >= 7 {
return Err(MalformedPolyline(pos=index, reason="delta is too large"))
}
let code = encoded[index].to_int() - 63
if code < 0 || code > 63 {
return Err(
MalformedPolyline(
pos=index,
reason="character is outside polyline alphabet",
),
)
}
result = result + code % 0x20 * factor
factor = factor * 0x20
index = index + 1
groups = groups + 1
if code < 0x20 {
let delta = if result % 2 == 1 { -(result + 1) / 2 } else { result / 2 }
return Ok((delta, index))
}
}
Err(MalformedPolyline(pos=start, reason="unterminated delta"))
}