Last active
April 13, 2025 15:36
-
-
Save michael-simons/8d47454c9586db88fb3a1cc7b8ce1cf9 to your computer and use it in GitHub Desktop.
If you ever want to encode a poly line against Google Earth / OSM Valhalla standards
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.stream.Collectors; | |
import java.util.stream.Gatherers; | |
public final class Polyline { | |
private final List<Point> points = new ArrayList<>(); | |
private final int scale; | |
// Valhalla uses a scale of 6, Google is stupid and uses 5. | |
Polyline() { | |
this(6); | |
} | |
Polyline(int scale) { | |
this.scale = (int) Math.pow(10, scale); | |
} | |
void add(double x, double y) { | |
this.points.add(new Point((int) Math.floor(x * this.scale), (int) Math.floor(y * this.scale))); | |
} | |
@Override | |
public String toString() { | |
if (this.points.isEmpty()) { | |
return ""; | |
} | |
var initial = this.points.getFirst().encode(); | |
if (this.points.size() == 1) { | |
return initial; | |
} | |
var remainder = this.points.stream() | |
.gather(Gatherers.windowSliding(2)) | |
.map(pair -> pair.getLast().offset(pair.getFirst()).encode()) | |
.collect(Collectors.joining()); | |
return initial + remainder; | |
} | |
public static void main(String... a) { | |
var polyline = new Polyline(5); | |
polyline.add(38.5, -120.2); | |
polyline.add(40.7, -120.95); | |
polyline.add(43.252, -126.453); | |
System.err.println(polyline); | |
} | |
record Point(int x, int y) { | |
private Point offset(Point other) { | |
return new Point(this.x - other.x(), this.y - other.y()); | |
} | |
private String encode() { | |
return encode0(this.x) + encode0(this.y); | |
} | |
private String encode0(int v) { | |
var num = v << 1; | |
if (v < 0) { | |
num = ~num; | |
} | |
var result = new StringBuilder(); | |
while (num >= 0x20) { | |
int nextValue = (0x20 | (num & 0x1f)) + 63; | |
result.append((char) (nextValue)); | |
num >>= 5; | |
} | |
num += 63; | |
result.append((char) (num)); | |
return result.toString(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment