Polyline
Encode and decode Google's Encoded Polyline Algorithm Format — lists of coordinates as compact ASCII strings.
Implements Encoded Polyline Algorithm Format. Spec v0.1.1; every port passes the same 44 conformance cases.
Julia registration pendingNim v0.1.0 · listing pendingZig v0.1.0
Install
using PkgPkg.add(url="https://github.com/Xenoglyphiq/EncodedPolyline.jl")nimble install https://github.com/Xenoglyphiq/polyline-nim@#v0.1.0zig fetch --save git+https://github.com/Xenoglyphiq/polyline-zig#v0.1.0const polyline = b.dependency("polyline", .{ .target = target, .optimize = optimize });exe.root_module.addImport("polyline", polyline.module("polyline"));Examples
The same three examples exist in every port, runnable as-is from each repository's examples/ folder.
Encode a three-point route
# Canonical example `encode_route`: encode a three-point route and print the string.using EncodedPolyline
route = [LonLat(-120.2, 38.5), LonLat(-120.95, 40.7), LonLat(-126.453, 43.252)]println(EncodedPolyline.encode(route))## Canonical example `encode_route`: encode a three-point route and print the string.import polyline
let route = [ LonLat(lon: -120.2, lat: 38.5), LonLat(lon: -120.95, lat: 40.7), LonLat(lon: -126.453, lat: 43.252),]echo encode(route)//! Canonical example `encode_route`: encode a three-point route and print the string.const std = @import("std");const polyline = @import("polyline");
pub fn main(init: std.process.Init) !void { const route = [_]polyline.LonLat{ .{ .lon = -120.2, .lat = 38.5 }, .{ .lon = -120.95, .lat = 40.7 }, .{ .lon = -126.453, .lat = 43.252 }, }; const text = try polyline.encode(init.gpa, &route, .{}, null); defer init.gpa.free(text); std.debug.print("{s}\n", .{text});}Decode Google's documented example
# Canonical example `decode_route`: decode Google's example and print each point.using EncodedPolyline
points = try EncodedPolyline.decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@")catch e e isa PolylineError && println(stderr, sprint(showerror, e)) rethrow()endfor p in points println("lon $(p.lon), lat $(p.lat)")end## Canonical example `decode_route`: decode Google's example and print each point.import polyline
try: for p in decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@"): echo "lon ", p.lon, ", lat ", p.latexcept PolylineError as e: stderr.writeLine e.kind, ": ", e.code, " at byte ", (if e.offset.isSome: $e.offset.get else: "?") quit 1//! Canonical example `decode_route`: decode Google's example and print each point.const std = @import("std");const polyline = @import("polyline");
pub fn main(init: std.process.Init) !void { var diag: polyline.Diagnostics = .{}; const points = polyline.decode(init.gpa, "_p~iF~ps|U_ulLnnqC_mqNvxq`@", .{}, &diag) catch |err| { std.debug.print("{s}: {s} at byte {?d}\n", .{ @errorName(err), diag.code, diag.offset }); return err; }; defer init.gpa.free(points); for (points) |p| std.debug.print("lon {d}, lat {d}\n", .{ p.lon, p.lat });}Round-trip a route at precision 6
# Canonical example `precision_6`: round-trip a route at precision 6 (OSRM, Valhalla).using EncodedPolyline
route = [LonLat(-73.985713, 40.748441), LonLat(-73.978569, 40.751657), LonLat(-73.968285, 40.785091)]text = EncodedPolyline.encode(route; precision=6)back = EncodedPolyline.decode(text; precision=6)
println(text)for (a, b) in zip(route, back) println("($(a.lon), $(a.lat)) -> ($(b.lon), $(b.lat))") abs(a.lon - b.lon) <= 1e-6 && abs(a.lat - b.lat) <= 1e-6 || error("round trip mismatch")endprintln("round trip matches")## Canonical example `precision_6`: round-trip a route at precision 6 (OSRM, Valhalla).import polyline
let route = [ LonLat(lon: -73.985713, lat: 40.748441), LonLat(lon: -73.978569, lat: 40.751657), LonLat(lon: -73.968285, lat: 40.785091),]let text = encode(route, precision = 6)let back = decode(text, precision = 6)
echo textfor i, a in route: let b = back[i] echo "(", a.lon, ", ", a.lat, ") -> (", b.lon, ", ", b.lat, ")" if abs(a.lon - b.lon) > 1e-6 or abs(a.lat - b.lat) > 1e-6: quit "round trip mismatch", 1echo "round trip matches"//! Canonical example `precision_6`: round-trip a route at precision 6 (OSRM, Valhalla).const std = @import("std");const polyline = @import("polyline");
pub fn main(init: std.process.Init) !void { const route = [_]polyline.LonLat{ .{ .lon = -73.985713, .lat = 40.748441 }, .{ .lon = -73.978569, .lat = 40.751657 }, .{ .lon = -73.968285, .lat = 40.785091 }, }; const opts: polyline.Options = .{ .precision = 6 }; const text = try polyline.encode(init.gpa, &route, opts, null); defer init.gpa.free(text); const back = try polyline.decode(init.gpa, text, opts, null); defer init.gpa.free(back);
std.debug.print("{s}\n", .{text}); for (route, back) |a, b| { std.debug.print("({d}, {d}) -> ({d}, {d})\n", .{ a.lon, a.lat, b.lon, b.lat }); if (@abs(a.lon - b.lon) > 1e-6 or @abs(a.lat - b.lat) > 1e-6) return error.RoundTripMismatch; } std.debug.print("round trip matches\n", .{});}Ports
| Language | Package | Release | Spec | Conformance |
|---|---|---|---|---|
| Julia | EncodedPolyline | registration pending | v0.1.1 | core ✓ full ✓ (44/44) |
| Nim | polyline | v0.1.0 · listing pending | v0.1.1 | core ✓ full ✓ (44/44) |
| Zig | polyline | v0.1.0 | v0.1.1 | core ✓ full ✓ (44/44) |
Performance
Median time, and ratio to the reference implementation measured in the same session. The spec's target is within 2×.
| Port | Encode 100k points | Decode 100k points |
|---|---|---|
| Reference | Rust polyline 0.11.0: 0.892 ms | Rust polyline 0.11.0: 0.643 ms |
| Julia | 1.466 ms (1.64×) | 0.816 ms (1.27×) |
| Nim | 1.645 ms (1.84×) | 0.802 ms (1.25×) |
| Zig | 1.308 ms (1.47×) | 0.642 ms (1.00×) |
Spec and source
- Behavior spec and machine-readable contract
- Conformance cases (44)
- Decisions and benchmark method
- Julia port: Xenoglyphiq/EncodedPolyline.jl
- Nim port: Xenoglyphiq/polyline-nim
- Zig port: Xenoglyphiq/polyline-zig