diff --git a/enabler/src/de/schildbach/pte/AbstractHafasClientInterfaceProvider.java b/enabler/src/de/schildbach/pte/AbstractHafasClientInterfaceProvider.java index a96b1f63..7e8f36bb 100644 --- a/enabler/src/de/schildbach/pte/AbstractHafasClientInterfaceProvider.java +++ b/enabler/src/de/schildbach/pte/AbstractHafasClientInterfaceProvider.java @@ -78,6 +78,7 @@ import de.schildbach.pte.dto.Trip; import de.schildbach.pte.dto.TripOptions; import de.schildbach.pte.exception.ParserException; import de.schildbach.pte.util.ParserUtils; +import de.schildbach.pte.util.PolylineFormat; import okhttp3.HttpUrl; @@ -552,7 +553,7 @@ public abstract class AbstractHafasClientInterfaceProvider extends AbstractHafas ? "\"jnyFltrL\":[{\"value\":\"" + jnyFltr + "\",\"mode\":\"BIT\",\"type\":\"PROD\"}]," : "") // + "\"gisFltrL\":[{\"mode\":\"FB\",\"profile\":{\"type\":\"F\",\"linDistRouting\":false,\"maxdist\":2000},\"type\":\"M\",\"meta\":\"" + meta + "\"}]," // - + "\"getPolyline\":false,\"getPasslist\":true,\"getIST\":false,\"getEco\":false,\"extChgTime\":-1}", // + + "\"getPolyline\":true,\"getPasslist\":true,\"getIST\":false,\"getEco\":false,\"extChgTime\":-1}", // false); final HttpUrl url = requestUrl(request); @@ -620,6 +621,7 @@ public abstract class AbstractHafasClientInterfaceProvider extends AbstractHafas final JSONArray locList = common.getJSONArray("locL"); final List operators = parseOpList(common.getJSONArray("opL")); final List lines = parseProdList(common.getJSONArray("prodL"), operators, styles); + final List encodedPolylines = parsePolyList(common.getJSONArray("polyL")); final JSONArray outConList = res.optJSONArray("outConL"); final List trips = new ArrayList<>(outConList.length()); @@ -673,6 +675,27 @@ public abstract class AbstractHafasClientInterfaceProvider extends AbstractHafas intermediateStops = null; } + final List path; + final JSONObject polyG = jny.optJSONObject("polyG"); + if (polyG != null) { + final int crdSysX = polyG.optInt("crdSysX", -1); + if (crdSysX != -1) { + final String crdSysType = crdSysList.getJSONObject(crdSysX).getString("type"); + if (!"WGS84".equals(crdSysType)) + throw new RuntimeException("unknown type: " + crdSysType); + } + final JSONArray polyXList = polyG.getJSONArray("polyXL"); + path = new LinkedList<>(); + final int polyXListLen = polyXList.length(); + checkState(polyXListLen <= 1); + for (int i = 0; i < polyXListLen; i++) { + final String encodedPolyline = encodedPolylines.get(polyXList.getInt(i)); + path.addAll(PolylineFormat.decode(encodedPolyline)); + } + } else { + path = null; + } + final JSONArray remList = jny.optJSONArray("remL"); String message = null; if (remList != null) { @@ -684,7 +707,7 @@ public abstract class AbstractHafasClientInterfaceProvider extends AbstractHafas } } - leg = new Trip.Public(line, destination, departureStop, arrivalStop, intermediateStops, null, + leg = new Trip.Public(line, destination, departureStop, arrivalStop, intermediateStops, path, message); } else if ("DEVI".equals(secType)) { leg = new Trip.Individual(Trip.Individual.Type.TRANSFER, departureStop.location, @@ -1036,6 +1059,18 @@ public abstract class AbstractHafasClientInterfaceProvider extends AbstractHafas return lines; } + private List parsePolyList(final JSONArray polyList) throws JSONException { + final int len = polyList.length(); + final List polylines = new ArrayList<>(len); + + for (int i = 0; i < len; i++) { + final JSONObject poly = polyList.getJSONObject(i); + checkState(poly.getBoolean("delta")); + polylines.add(poly.getString("crdEncYX")); + } + return polylines; + } + protected Line newLine(final String id, final String operator, final Product product, final @Nullable String name, final @Nullable String shortName, final @Nullable String number, final Style style) { final String longName; diff --git a/enabler/src/de/schildbach/pte/dto/Point.java b/enabler/src/de/schildbach/pte/dto/Point.java index 6719ba22..e00cf7a3 100644 --- a/enabler/src/de/schildbach/pte/dto/Point.java +++ b/enabler/src/de/schildbach/pte/dto/Point.java @@ -43,6 +43,10 @@ public final class Point implements Serializable { return new Point(lat / 1E6, lon / 1E6); } + public static Point from1E5(final int lat, final int lon) { + return new Point(lat / 1E5, lon / 1E5); + } + public double getLatAsDouble() { return lat; } @@ -59,6 +63,14 @@ public final class Point implements Serializable { return (int) Math.round(lon * 1E6); } + public int getLatAs1E5() { + return (int) Math.round(lat * 1E5); + } + + public int getLonAs1E5() { + return (int) Math.round(lon * 1E5); + } + @Override public boolean equals(final Object o) { if (o == this) diff --git a/enabler/src/de/schildbach/pte/util/PolylineFormat.java b/enabler/src/de/schildbach/pte/util/PolylineFormat.java new file mode 100644 index 00000000..a77745b7 --- /dev/null +++ b/enabler/src/de/schildbach/pte/util/PolylineFormat.java @@ -0,0 +1,67 @@ +/* + * Copyright the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.schildbach.pte.util; + +import java.util.ArrayList; +import java.util.List; + +import de.schildbach.pte.dto.Point; + +/** + *

+ * Implementation of the + * Encoded Polyline + * Algorithm Format. + *

+ * + * @author Andreas Schildbach + */ +public final class PolylineFormat { + public static List decode(final String encodedPolyline) { + final int len = encodedPolyline.length(); + final List path = new ArrayList<>(len / 2); + + int lat = 0; + int lon = 0; + int index = 0; + while (index < len) { + int latResult = 1; + int latShift = 0; + int latB; + do { + latB = encodedPolyline.charAt(index++) - 63 - 1; + latResult += latB << latShift; + latShift += 5; + } while (latB >= 0x1f); + lat += (latResult & 1) != 0 ? ~(latResult >> 1) : (latResult >> 1); + + int lonResult = 1; + int lonShift = 0; + int lonB; + do { + lonB = encodedPolyline.charAt(index++) - 63 - 1; + lonResult += lonB << lonShift; + lonShift += 5; + } while (lonB >= 0x1f); + lon += (lonResult & 1) != 0 ? ~(lonResult >> 1) : (lonResult >> 1); + + path.add(Point.from1E5(lat, lon)); + } + return path; + } +} diff --git a/enabler/test/de/schildbach/pte/util/PolylineFormatTest.java b/enabler/test/de/schildbach/pte/util/PolylineFormatTest.java new file mode 100644 index 00000000..c909424d --- /dev/null +++ b/enabler/test/de/schildbach/pte/util/PolylineFormatTest.java @@ -0,0 +1,40 @@ +/* + * Copyright the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.schildbach.pte.util; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; + +import de.schildbach.pte.dto.Point; + +/** + * @author Andreas Schildbach + */ +public class PolylineFormatTest { + @Test + public void test() { + final List polyline = PolylineFormat.decode( + "}qfeHyn|bBnBdA\\R]xBzA|@r@f@u@hCWS{@bCe@t@e@v@h@vCIFu@`@MPDJ@L?NAPIZXf@|@`Br@pAHLZp@~@jBbArBbBjDLTTd@fAzBcFnH[d@Vf@iA`BWb@t@zAb@~@LTNNdCzE~A{BAA??"); + assertEquals(44, polyline.size()); + assertEquals(Point.fromDouble(48.2078300, 16.3711700), polyline.get(0)); + assertEquals(Point.fromDouble(48.2051400, 16.3579600), polyline.get(43)); + } +}