From 875bd1d6510707e1e207684cafc232072ceab592 Mon Sep 17 00:00:00 2001 From: "andreas.schildbach" Date: Fri, 8 Oct 2010 17:43:57 +0000 Subject: [PATCH] rudimentary connections for all efa based providers git-svn-id: https://public-transport-enabler.googlecode.com/svn/trunk@246 0924bc21-9374-b0fa-ee44-9ff1593b38f0 --- .../schildbach/pte/AbstractEfaProvider.java | 305 +++++++++++++++--- src/de/schildbach/pte/BahnProvider.java | 14 +- src/de/schildbach/pte/GvhProvider.java | 96 +++++- src/de/schildbach/pte/LinzProvider.java | 99 +++++- src/de/schildbach/pte/MvvProvider.java | 42 +-- src/de/schildbach/pte/OebbProvider.java | 14 +- src/de/schildbach/pte/RmvProvider.java | 16 +- src/de/schildbach/pte/SbbProvider.java | 14 +- src/de/schildbach/pte/VbbProvider.java | 12 +- src/de/schildbach/pte/VrnProvider.java | 96 +++++- src/de/schildbach/pte/VrrProvider.java | 96 +++++- src/de/schildbach/pte/dto/Connection.java | 10 +- .../pte/dto/QueryConnectionsResult.java | 79 +++-- .../pte/live/GvhProviderLiveTest.java | 13 + .../pte/live/LinzProviderLiveTest.java | 12 +- 15 files changed, 725 insertions(+), 193 deletions(-) diff --git a/src/de/schildbach/pte/AbstractEfaProvider.java b/src/de/schildbach/pte/AbstractEfaProvider.java index 035438af..0139d8ca 100644 --- a/src/de/schildbach/pte/AbstractEfaProvider.java +++ b/src/de/schildbach/pte/AbstractEfaProvider.java @@ -22,8 +22,10 @@ import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Calendar; +import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -33,10 +35,14 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import de.schildbach.pte.dto.Autocomplete; +import de.schildbach.pte.dto.Connection; import de.schildbach.pte.dto.Departure; +import de.schildbach.pte.dto.GetConnectionDetailsResult; import de.schildbach.pte.dto.NearbyStationsResult; +import de.schildbach.pte.dto.QueryConnectionsResult; import de.schildbach.pte.dto.QueryDeparturesResult; import de.schildbach.pte.dto.Station; +import de.schildbach.pte.dto.QueryConnectionsResult.Status; import de.schildbach.pte.util.Color; import de.schildbach.pte.util.ParserUtils; import de.schildbach.pte.util.XmlPullUtil; @@ -69,17 +75,7 @@ public abstract class AbstractEfaProvider implements NetworkProvider XmlPullUtil.nextStartTagInsideTree(pp, null, "itdOdvName"); final String nameState = pp.getAttributeValue(null, "state"); if ("list".equals(nameState)) - { - while (XmlPullUtil.nextStartTagInsideTree(pp, null, "odvNameElem")) - { - final String type = pp.getAttributeValue(null, "anyType"); - int id = Integer.parseInt(pp.getAttributeValue(null, "id")); - if (id < 0) - id = 0; - final String name = normalizeLocationName(pp.nextText()); - results.add(new Autocomplete(type(type), id, name)); - } - } + processOdvNameElem(results, pp); // parse assigned stops if (XmlPullUtil.jumpToStartTag(pp, null, "itdOdvAssignedStops")) @@ -105,16 +101,33 @@ public abstract class AbstractEfaProvider implements NetworkProvider } } + private void processOdvNameElem(final List results, final XmlPullParser pp) throws XmlPullParserException, IOException + { + while (XmlPullUtil.nextStartTagInsideTree(pp, null, "odvNameElem")) + { + final String type = pp.getAttributeValue(null, "anyType"); + int id = Integer.parseInt(pp.getAttributeValue(null, "id")); + if (id < 0) + id = 0; + final String name = normalizeLocationName(pp.nextText()); + results.add(new Autocomplete(type(type), id, name)); + } + } + private static LocationType type(final String type) { if (type.equals("stop")) return LocationType.STATION; if (type.equals("poi")) return LocationType.POI; - if (type.equals("street")) - return LocationType.ADDRESS; if (type.equals("loc")) return LocationType.ANY; + if (type.equals("street")) + return LocationType.ADDRESS; + if (type.equals("singlehouse")) + return LocationType.ADDRESS; + if (type.equals("address")) + return LocationType.ADDRESS; throw new IllegalArgumentException("unknown type: " + type); } @@ -235,16 +248,16 @@ public abstract class AbstractEfaProvider implements NetworkProvider private static final Pattern P_LINE_U = Pattern.compile("U\\d+"); private static final Pattern P_LINE_NUMBER = Pattern.compile("\\d+"); - protected String parseLine(final String number, final String symbol, final String mot) + protected String parseLine(final String mot, final String name, final String longName) { - if (!number.equals(symbol)) - throw new IllegalStateException("number " + number + ", symbol " + symbol); + if (mot == null || name == null || longName == null) + throw new IllegalStateException("cannot normalize mot '" + mot + "' name '" + name + "' long '" + longName + "'"); final int t = Integer.parseInt(mot); if (t == 0) { - final String[] parts = number.split(" ", 3); + final String[] parts = longName.split(" ", 3); final String type = parts[0]; final String num = parts.length >= 2 ? parts[1] : null; final String str = type + (num != null ? num : ""); @@ -451,24 +464,24 @@ public abstract class AbstractEfaProvider implements NetworkProvider if (P_LINE_NUMBER.matcher(type).matches()) return "?"; - throw new IllegalArgumentException("cannot normalize: " + number); + throw new IllegalArgumentException("cannot normalize: " + longName); } if (t == 1) - return 'S' + number; + return 'S' + name; if (t == 2) - return 'U' + number; + return 'U' + name; if (t == 3 || t == 4) - return 'T' + number; + return 'T' + name; if (t == 5 || t == 6 || t == 7 || t == 10) - return 'B' + number; + return 'B' + name; if (t == 8) - return 'C' + number; + return 'C' + name; if (t == 9) - return 'F' + number; + return 'F' + name; if (t == 11) - return '?' + number; + return '?' + name; - throw new IllegalStateException("cannot normalize mot '" + mot + "' number '" + number + "'"); + throw new IllegalStateException("cannot normalize mot '" + mot + "' name '" + name + "' long '" + longName + "'"); } public QueryDeparturesResult queryDepartures(final String uri) throws IOException @@ -512,25 +525,12 @@ public abstract class AbstractEfaProvider implements NetworkProvider departureTime.clear(); - if (!XmlPullUtil.nextStartTagInsideTree(pp, null, "itdDateTime")) - throw new IllegalStateException("itdDateTime not found:" + pp.getPositionDescription()); - - if (!XmlPullUtil.nextStartTagInsideTree(pp, null, "itdDate")) - throw new IllegalStateException("itdDate not found:" + pp.getPositionDescription()); - processItdDate(pp, departureTime); - XmlPullUtil.skipRestOfTree(pp); - - if (!XmlPullUtil.nextStartTagInsideTree(pp, null, "itdTime")) - throw new IllegalStateException("itdTime not found:" + pp.getPositionDescription()); - processItdTime(pp, departureTime); - XmlPullUtil.skipRestOfTree(pp); - - XmlPullUtil.skipRestOfTree(pp); + processItdDateTime(pp, departureTime); if (!XmlPullUtil.nextStartTagInsideTree(pp, null, "itdServingLine")) throw new IllegalStateException("itdServingLine not found:" + pp.getPositionDescription()); - final String line = parseLine(pp.getAttributeValue(null, "number"), pp.getAttributeValue(null, "symbol"), pp - .getAttributeValue(null, "motType")); + final String line = parseLine(pp.getAttributeValue(null, "motType"), pp.getAttributeValue(null, "number"), pp + .getAttributeValue(null, "number")); final boolean isRealtime = pp.getAttributeValue(null, "realtime").equals("1"); final String destination = normalizeLocationName(pp.getAttributeValue(null, "direction")); final int destinationId = Integer.parseInt(pp.getAttributeValue(null, "destID")); @@ -564,6 +564,24 @@ public abstract class AbstractEfaProvider implements NetworkProvider } } + private void processItdDateTime(final XmlPullParser pp, final Calendar calendar) throws XmlPullParserException, IOException + { + if (!XmlPullUtil.nextStartTagInsideTree(pp, null, "itdDateTime")) + throw new IllegalStateException("itdDateTime not found:" + pp.getPositionDescription()); + + if (!XmlPullUtil.nextStartTagInsideTree(pp, null, "itdDate")) + throw new IllegalStateException("itdDate not found:" + pp.getPositionDescription()); + processItdDate(pp, calendar); + XmlPullUtil.skipRestOfTree(pp); + + if (!XmlPullUtil.nextStartTagInsideTree(pp, null, "itdTime")) + throw new IllegalStateException("itdTime not found:" + pp.getPositionDescription()); + processItdTime(pp, calendar); + XmlPullUtil.skipRestOfTree(pp); + + XmlPullUtil.skipRestOfTree(pp); + } + private void processItdDate(final XmlPullParser pp, final Calendar calendar) throws XmlPullParserException, IOException { pp.require(XmlPullParser.START_TAG, null, "itdDate"); @@ -575,7 +593,7 @@ public abstract class AbstractEfaProvider implements NetworkProvider private void processItdTime(final XmlPullParser pp, final Calendar calendar) throws XmlPullParserException, IOException { pp.require(XmlPullParser.START_TAG, null, "itdTime"); - calendar.set(Calendar.HOUR, Integer.parseInt(pp.getAttributeValue(null, "hour"))); + calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(pp.getAttributeValue(null, "hour"))); calendar.set(Calendar.MINUTE, Integer.parseInt(pp.getAttributeValue(null, "minute"))); } @@ -591,7 +609,197 @@ public abstract class AbstractEfaProvider implements NetworkProvider return (double) value / 1000000; } - protected final String productParams(final String products) + public QueryConnectionsResult queryConnections(final LocationType fromType, final String from, final LocationType viaType, final String via, + final LocationType toType, final String to, final Date date, final boolean dep, final String products, final WalkSpeed walkSpeed) + throws IOException + { + final String uri = connectionsQueryUri(fromType, from, viaType, via, toType, to, date, dep, products, walkSpeed) + "&sessionID=0"; + + final CharSequence page = ParserUtils.scrape(uri); + + return queryConnections(uri, page); + } + + public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException + { + final CharSequence page = ParserUtils.scrape(uri); + + return queryConnections(uri, page); + } + + private QueryConnectionsResult queryConnections(final String uri, final CharSequence page) throws IOException + { + try + { + final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); + final XmlPullParser pp = factory.newPullParser(); + pp.setInput(new StringReader(page.toString())); + + XmlPullUtil.jumpToStartTag(pp, null, "itdRequest"); + final String sessionId = pp.getAttributeValue(null, "sessionID"); + + // parse odv name elements + List ambiguousFrom = null, ambiguousTo = null, ambiguousVia = null; + String from = null, to = null; + + XmlPullUtil.jumpToStartTag(pp, null, "itdOdv"); + if (!"origin".equals(pp.getAttributeValue(null, "usage"))) + throw new IllegalStateException(); + XmlPullUtil.nextStartTagInsideTree(pp, null, "itdOdvName"); + final String originState = pp.getAttributeValue(null, "state"); + if ("list".equals(originState)) + { + ambiguousFrom = new ArrayList(); + processOdvNameElem(ambiguousFrom, pp); + } + else if ("identified".equals(originState)) + { + XmlPullUtil.nextStartTagInsideTree(pp, null, "odvNameElem"); + from = pp.nextText(); + } + + XmlPullUtil.jumpToStartTag(pp, null, "itdOdv"); + if (!"destination".equals(pp.getAttributeValue(null, "usage"))) + throw new IllegalStateException(); + XmlPullUtil.nextStartTagInsideTree(pp, null, "itdOdvName"); + final String destinationState = pp.getAttributeValue(null, "state"); + if ("list".equals(destinationState)) + { + ambiguousTo = new ArrayList(); + processOdvNameElem(ambiguousTo, pp); + } + else if ("identified".equals(destinationState)) + { + XmlPullUtil.nextStartTagInsideTree(pp, null, "odvNameElem"); + to = pp.nextText(); + } + + XmlPullUtil.jumpToStartTag(pp, null, "itdOdv"); + if (!"via".equals(pp.getAttributeValue(null, "usage"))) + throw new IllegalStateException(); + XmlPullUtil.nextStartTagInsideTree(pp, null, "itdOdvName"); + final String viaState = pp.getAttributeValue(null, "state"); + if ("list".equals(viaState)) + { + ambiguousVia = new ArrayList(); + processOdvNameElem(ambiguousVia, pp); + } + else if ("identified".equals(viaState)) + { + // TODO parse identified name/id + } + + if (ambiguousFrom != null || ambiguousTo != null || ambiguousVia != null) + return new QueryConnectionsResult(ambiguousFrom, ambiguousVia, ambiguousTo); + + final Calendar departureTime = new GregorianCalendar(), arrivalTime = new GregorianCalendar(); + final List connections = new ArrayList(); + + if (XmlPullUtil.jumpToStartTag(pp, null, "itdRouteList")) + { + while (XmlPullUtil.nextStartTagInsideTree(pp, null, "itdRoute")) + { + final String id = pp.getAttributeValue(null, "routeIndex") + "-" + pp.getAttributeValue(null, "routeTripIndex"); + + XmlPullUtil.jumpToStartTag(pp, null, "itdPartialRouteList"); + final List parts = new LinkedList(); + String firstDeparture = null; + Date firstDepartureTime = null; + String lastArrival = null; + Date lastArrivalTime = null; + + while (XmlPullUtil.nextStartTagInsideTree(pp, null, "itdPartialRoute")) + { + XmlPullUtil.jumpToStartTag(pp, null, "itdPoint"); + if (!"departure".equals(pp.getAttributeValue(null, "usage"))) + throw new IllegalStateException(); + final int departureId = Integer.parseInt(pp.getAttributeValue(null, "stopID")); + final String departure = normalizeLocationName(pp.getAttributeValue(null, "name")); + if (firstDeparture == null) + firstDeparture = departure; + final String departurePosition = pp.getAttributeValue(null, "platform"); + processItdDateTime(pp, departureTime); + if (firstDepartureTime == null) + firstDepartureTime = departureTime.getTime(); + XmlPullUtil.skipRestOfTree(pp); + + XmlPullUtil.jumpToStartTag(pp, null, "itdPoint"); + if (!"arrival".equals(pp.getAttributeValue(null, "usage"))) + throw new IllegalStateException(); + final int arrivalId = Integer.parseInt(pp.getAttributeValue(null, "stopID")); + final String arrival = normalizeLocationName(pp.getAttributeValue(null, "name")); + lastArrival = arrival; + final String arrivalPosition = pp.getAttributeValue(null, "platform"); + processItdDateTime(pp, arrivalTime); + lastArrivalTime = arrivalTime.getTime(); + XmlPullUtil.skipRestOfTree(pp); + + XmlPullUtil.jumpToStartTag(pp, null, "itdMeansOfTransport"); + Connection.Part part; + if (!"Fussweg".equals(pp.getAttributeValue(null, "productName"))) + { + final String destinationIdStr = pp.getAttributeValue(null, "destID"); + final int destinationId = destinationIdStr.length() > 0 ? Integer.parseInt(destinationIdStr) : 0; + final String destination = normalizeLocationName(pp.getAttributeValue(null, "destination")); + final String line = parseLine(pp.getAttributeValue(null, "motType"), pp.getAttributeValue(null, "shortname"), pp + .getAttributeValue(null, "name")); + part = new Connection.Trip(line, LINES.get(line.charAt(0)), destinationId, destination, departureTime.getTime(), + departurePosition, departureId, departure, arrivalTime.getTime(), arrivalPosition, arrivalId, arrival); + } + else + { + final int min = (int) (arrivalTime.getTimeInMillis() - departureTime.getTimeInMillis()) / 1000 / 60; + part = new Connection.Footway(min, departureId, departurePosition, arrivalId, arrivalPosition); + } + XmlPullUtil.skipRestOfTree(pp); + + parts.add(part); + XmlPullUtil.skipRestOfTree(pp); + } + + connections + .add(new Connection(id, uri, firstDepartureTime, lastArrivalTime, null, null, 0, firstDeparture, 0, lastArrival, parts)); + XmlPullUtil.skipRestOfTree(pp); + } + + return new QueryConnectionsResult(uri, from, to, null, commandLink(sessionId, "tripPrev"), commandLink(sessionId, "tripNext"), + connections); + } + else + { + return new QueryConnectionsResult(Status.NO_CONNECTIONS); + } + } + catch (final XmlPullParserException x) + { + throw new RuntimeException(x); + } + } + + public GetConnectionDetailsResult getConnectionDetails(final String connectionUri) throws IOException + { + throw new UnsupportedOperationException(); + } + + protected abstract String connectionsQueryUri(LocationType fromType, String from, LocationType viaType, String via, LocationType toType, + String to, Date date, boolean dep, String products, WalkSpeed walkSpeed); + + protected abstract String commandLink(String sessionId, String command); + + protected static final String locationTypeValue(final LocationType locationType) + { + if (locationType == LocationType.STATION) + return "stop"; + if (locationType == LocationType.ADDRESS) + return "any"; // strange, matches with anyObjFilter + if (locationType == LocationType.POI) + return "any"; + if (locationType == LocationType.ANY) + return "any"; + throw new IllegalArgumentException(locationType.toString()); + } + + protected static final String productParams(final String products) { if (products == null) return ""; @@ -620,6 +828,15 @@ public abstract class AbstractEfaProvider implements NetworkProvider return params.toString(); } + protected static final Map WALKSPEED_MAP = new HashMap(); + + static + { + WALKSPEED_MAP.put(WalkSpeed.SLOW, "slow"); + WALKSPEED_MAP.put(WalkSpeed.NORMAL, "normal"); + WALKSPEED_MAP.put(WalkSpeed.FAST, "fast"); + } + private static final Map LINES = new HashMap(); static diff --git a/src/de/schildbach/pte/BahnProvider.java b/src/de/schildbach/pte/BahnProvider.java index 992a3bd9..1dcb0159 100644 --- a/src/de/schildbach/pte/BahnProvider.java +++ b/src/de/schildbach/pte/BahnProvider.java @@ -187,9 +187,9 @@ public final class BahnProvider implements NetworkProvider return QueryConnectionsResult.INVALID_DATE; } - List fromAddresses = null; - List viaAddresses = null; - List toAddresses = null; + List fromAddresses = null; + List viaAddresses = null; + List toAddresses = null; final Matcher mPreAddress = P_PRE_ADDRESS.matcher(page); while (mPreAddress.find()) @@ -198,12 +198,12 @@ public final class BahnProvider implements NetworkProvider final String options = mPreAddress.group(2); final Matcher mAddresses = P_ADDRESSES.matcher(options); - final List addresses = new ArrayList(); + final List addresses = new ArrayList(); while (mAddresses.find()) { final String address = ParserUtils.resolveEntities(mAddresses.group(1)).trim(); if (!addresses.contains(address)) - addresses.add(address); + addresses.add(new Autocomplete(LocationType.ANY, 0, address)); } if (type.equals("REQ0JourneyStopsS0K")) @@ -217,7 +217,7 @@ public final class BahnProvider implements NetworkProvider } if (fromAddresses != null || viaAddresses != null || toAddresses != null) - return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, fromAddresses, viaAddresses, toAddresses); + return new QueryConnectionsResult(fromAddresses, viaAddresses, toAddresses); else return queryConnections(uri, page); } @@ -371,7 +371,7 @@ public final class BahnProvider implements NetworkProvider final Date departureDateTime = ParserUtils.joinDateTime(departureDate, departureTime); final Date arrivalDateTime = ParserUtils.joinDateTime(arrivalDate, arrivalTime); - lastTrip = new Connection.Trip(line, line != null ? LINES.get(line.charAt(0)) : null, null, departureDateTime, + lastTrip = new Connection.Trip(line, line != null ? LINES.get(line.charAt(0)) : null, 0, null, departureDateTime, departurePosition, 0, departure, arrivalDateTime, arrivalPosition, 0, arrival); parts.add(lastTrip); diff --git a/src/de/schildbach/pte/GvhProvider.java b/src/de/schildbach/pte/GvhProvider.java index 057005d6..86d1a97a 100644 --- a/src/de/schildbach/pte/GvhProvider.java +++ b/src/de/schildbach/pte/GvhProvider.java @@ -17,11 +17,10 @@ package de.schildbach.pte; -import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Date; -import de.schildbach.pte.dto.GetConnectionDetailsResult; -import de.schildbach.pte.dto.QueryConnectionsResult; import de.schildbach.pte.util.ParserUtils; /** @@ -35,7 +34,8 @@ public class GvhProvider extends AbstractEfaProvider public boolean hasCapabilities(final Capability... capabilities) { for (final Capability capability : capabilities) - if (capability == Capability.DEPARTURES) + if (capability == Capability.DEPARTURES || capability == Capability.CONNECTIONS || capability == Capability.LOCATION_STATION_ID + || capability == Capability.LOCATION_WGS84) return true; return false; @@ -77,19 +77,87 @@ public class GvhProvider extends AbstractEfaProvider return uri.toString(); } - public QueryConnectionsResult queryConnections(LocationType fromType, String from, LocationType viaType, String via, LocationType toType, - String to, Date date, boolean dep, String products, WalkSpeed walkSpeed) throws IOException + @Override + protected String connectionsQueryUri(final LocationType fromType, final String from, final LocationType viaType, final String via, + final LocationType toType, final String to, final Date date, final boolean dep, final String products, final WalkSpeed walkSpeed) { - throw new UnsupportedOperationException(); + final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); + final DateFormat TIME_FORMAT = new SimpleDateFormat("HHmm"); + + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE); + uri.append("XSLT_TRIP_REQUEST2"); + + uri.append("?language=de"); + uri.append("&outputFormat=XML"); + uri.append("&coordOutputFormat=WGS84"); + + if (fromType == LocationType.WGS84) + { + final String[] parts = from.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_origin=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_origin=coord"); + } + else + { + uri.append("&type_origin=").append(locationTypeValue(fromType)); + uri.append("&name_origin=").append(ParserUtils.urlEncode(from, "ISO-8859-1")); // fine-grained location + } + + if (toType == LocationType.WGS84) + { + final String[] parts = to.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_destination=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_destination=coord"); + } + else + { + uri.append("&type_destination=").append(locationTypeValue(toType)); + uri.append("&name_destination=").append(ParserUtils.urlEncode(to, "ISO-8859-1")); // fine-grained location + } + + if (via != null) + { + if (viaType == LocationType.WGS84) + { + final String[] parts = via.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_via=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_via=coord"); + } + else + { + uri.append("&type_via=").append(locationTypeValue(viaType)); + uri.append("&name_via=").append(ParserUtils.urlEncode(via, "ISO-8859-1")); + } + } + + uri.append("&itdDate=").append(ParserUtils.urlEncode(DATE_FORMAT.format(date))); + uri.append("&itdTime=").append(ParserUtils.urlEncode(TIME_FORMAT.format(date))); + uri.append("&itdTripDateTimeDepArr=").append(dep ? "dep" : "arr"); + + // TODO products + + uri.append("&changeSpeed=").append(WALKSPEED_MAP.get(walkSpeed)); + + uri.append("&locationServerActive=1"); + + return uri.toString(); } - public QueryConnectionsResult queryMoreConnections(String uri) throws IOException + @Override + protected String commandLink(final String sessionId, final String command) { - throw new UnsupportedOperationException(); - } - - public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException - { - throw new UnsupportedOperationException(); + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE); + uri.append("XSLT_TRIP_REQUEST2"); + uri.append("?sessionID=").append(sessionId); + uri.append("&command=").append(command); + return uri.toString(); } } diff --git a/src/de/schildbach/pte/LinzProvider.java b/src/de/schildbach/pte/LinzProvider.java index 38a25093..1899fcca 100644 --- a/src/de/schildbach/pte/LinzProvider.java +++ b/src/de/schildbach/pte/LinzProvider.java @@ -17,11 +17,10 @@ package de.schildbach.pte; -import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Date; -import de.schildbach.pte.dto.GetConnectionDetailsResult; -import de.schildbach.pte.dto.QueryConnectionsResult; import de.schildbach.pte.util.ParserUtils; /** @@ -35,7 +34,8 @@ public class LinzProvider extends AbstractEfaProvider public boolean hasCapabilities(final Capability... capabilities) { for (final Capability capability : capabilities) - if (capability == Capability.DEPARTURES) + if (capability == Capability.DEPARTURES || capability == Capability.CONNECTIONS || capability == Capability.LOCATION_STATION_ID + || capability == Capability.LOCATION_WGS84) return true; return false; @@ -69,21 +69,92 @@ public class LinzProvider extends AbstractEfaProvider return String.format(NEARBY_STATION_URI, stationId); } - public QueryConnectionsResult queryConnections(final LocationType fromType, final String from, final LocationType viaType, final String via, + @Override + protected String connectionsQueryUri(final LocationType fromType, final String from, final LocationType viaType, final String via, final LocationType toType, final String to, final Date date, final boolean dep, final String products, final WalkSpeed walkSpeed) - throws IOException { - throw new UnsupportedOperationException(); + final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); + final DateFormat TIME_FORMAT = new SimpleDateFormat("HHmm"); + + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE); + uri.append("XSLT_TRIP_REQUEST2"); + + uri.append("?language=de"); + uri.append("&outputFormat=XML"); + uri.append("&coordOutputFormat=WGS84"); + + if (fromType == LocationType.WGS84) + { + final String[] parts = from.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_origin=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_origin=coord"); + } + else + { + uri.append("&place_origin="); // coarse-grained location, e.g. city + uri.append("&placeState_origin=empty"); // empty|identified + uri.append("&type_origin=").append(locationTypeValue(fromType)); + uri.append("&name_origin=").append(ParserUtils.urlEncode(from, "ISO-8859-1")); // fine-grained location + } + + if (toType == LocationType.WGS84) + { + final String[] parts = to.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_destination=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_destination=coord"); + } + else + { + uri.append("&place_destination="); // coarse-grained location, e.g. city + uri.append("&placeState_destination=empty"); // empty|identified + uri.append("&type_destination=").append(locationTypeValue(toType)); + uri.append("&name_destination=").append(ParserUtils.urlEncode(to, "ISO-8859-1")); // fine-grained location + } + + if (via != null) + { + if (viaType == LocationType.WGS84) + { + final String[] parts = via.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_via=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_via=coord"); + } + else + { + uri.append("&place_via="); + uri.append("&placeState_via=empty"); + uri.append("&type_via=").append(locationTypeValue(viaType)); + uri.append("&name_via=").append(ParserUtils.urlEncode(via, "ISO-8859-1")); + } + } + + uri.append("&itdDate=").append(ParserUtils.urlEncode(DATE_FORMAT.format(date))); + uri.append("&itdTime=").append(ParserUtils.urlEncode(TIME_FORMAT.format(date))); + uri.append("&itdTripDateTimeDepArr=").append(dep ? "dep" : "arr"); + + // TODO products + + uri.append("&changeSpeed=").append(WALKSPEED_MAP.get(walkSpeed)); + + return uri.toString(); } - public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException + @Override + protected String commandLink(final String sessionId, final String command) { - throw new UnsupportedOperationException(); - } - - public GetConnectionDetailsResult getConnectionDetails(final String connectionUri) throws IOException - { - throw new UnsupportedOperationException(); + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE); + uri.append("XSLT_TRIP_REQUEST2"); + uri.append("?sessionID=").append(sessionId); + uri.append("&command=").append(command); + return uri.toString(); } public String departuresQueryUri(final String stationId, final int maxDepartures) diff --git a/src/de/schildbach/pte/MvvProvider.java b/src/de/schildbach/pte/MvvProvider.java index b00a7923..4490a492 100644 --- a/src/de/schildbach/pte/MvvProvider.java +++ b/src/de/schildbach/pte/MvvProvider.java @@ -174,15 +174,8 @@ public class MvvProvider extends AbstractEfaProvider return new NearbyStationsResult(uri, stations.subList(0, maxStations)); } - private static final Map WALKSPEED_MAP = new HashMap(); - static - { - WALKSPEED_MAP.put(WalkSpeed.SLOW, "slow"); - WALKSPEED_MAP.put(WalkSpeed.NORMAL, "normal"); - WALKSPEED_MAP.put(WalkSpeed.FAST, "fast"); - } - - private String connectionsQueryUri(final LocationType fromType, final String from, final LocationType viaType, final String via, + @Override + protected String connectionsQueryUri(final LocationType fromType, final String from, final LocationType viaType, final String via, final LocationType toType, final String to, final Date date, final boolean dep, final String products, final WalkSpeed walkSpeed) { final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); @@ -221,7 +214,7 @@ public class MvvProvider extends AbstractEfaProvider final String[] parts = from.split(",\\s*", 2); final int lat = Integer.parseInt(parts[0]); final int lon = Integer.parseInt(parts[1]); - uri.append("&nameInfo_origin=").append(String.format("%2.5f:%2.5f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.ddddd]"); + uri.append("&nameInfo_origin=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); uri.append("&typeInfo_origin=coord"); } else @@ -246,7 +239,7 @@ public class MvvProvider extends AbstractEfaProvider final String[] parts = to.split(",\\s*", 2); final int lat = Integer.parseInt(parts[0]); final int lon = Integer.parseInt(parts[1]); - uri.append("&nameInfo_destination=").append(String.format("%2.5f:%2.5f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.ddddd]"); + uri.append("&nameInfo_destination=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); uri.append("&typeInfo_destination=coord"); } else @@ -273,7 +266,7 @@ public class MvvProvider extends AbstractEfaProvider final String[] parts = via.split(",\\s*", 2); final int lat = Integer.parseInt(parts[0]); final int lon = Integer.parseInt(parts[1]); - uri.append("&nameInfo_via=").append(String.format("%2.5f:%2.5f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.ddddd]"); + uri.append("&nameInfo_via=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); uri.append("&typeInfo_via=coord"); } else @@ -336,8 +329,9 @@ public class MvvProvider extends AbstractEfaProvider private static final Pattern P_CHECK_CONNECTIONS_ERROR = Pattern.compile( "(Start und Ziel sind identisch)|(konnte keine Verbindung gefunden werden)", Pattern.CASE_INSENSITIVE); + @Override public QueryConnectionsResult queryConnections(final LocationType fromType, final String from, final LocationType viaType, final String via, - final LocationType toType, final String to, final Date date, final boolean dep, String products, final WalkSpeed walkSpeed) + final LocationType toType, final String to, final Date date, final boolean dep, final String products, final WalkSpeed walkSpeed) throws IOException { final String uri = connectionsQueryUri(fromType, from, viaType, via, toType, to, date, dep, products, walkSpeed); @@ -353,9 +347,9 @@ public class MvvProvider extends AbstractEfaProvider return QueryConnectionsResult.NO_CONNECTIONS; } - List fromAddresses = null; - List viaAddresses = null; - List toAddresses = null; + List fromAddresses = null; + List viaAddresses = null; + List toAddresses = null; final Matcher mPreAddress = P_PRE_ADDRESS.matcher(page); while (mPreAddress.find()) @@ -364,12 +358,12 @@ public class MvvProvider extends AbstractEfaProvider final String options = mPreAddress.group(2); final Matcher mAddresses = P_ADDRESSES.matcher(options); - final List addresses = new ArrayList(); + final List addresses = new ArrayList(); while (mAddresses.find()) { final String address = ParserUtils.resolveEntities(mAddresses.group(1)).trim(); if (!addresses.contains(address)) - addresses.add(address); + addresses.add(new Autocomplete(LocationType.ANY, 0, address)); } if (type.equals("name_origin")) @@ -383,11 +377,12 @@ public class MvvProvider extends AbstractEfaProvider } if (fromAddresses != null || viaAddresses != null || toAddresses != null) - return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, fromAddresses, viaAddresses, toAddresses); + return new QueryConnectionsResult(fromAddresses, viaAddresses, toAddresses); else return queryConnections(uri, page); } + @Override public QueryConnectionsResult queryMoreConnections(final String uri) throws IOException { final CharSequence page = ParserUtils.scrape(uri); @@ -501,6 +496,7 @@ public class MvvProvider extends AbstractEfaProvider + ").*?", Pattern.DOTALL); private static final Pattern P_CONNECTION_DETAILS_ERRORS = Pattern.compile("(session has expired)", Pattern.CASE_INSENSITIVE); + @Override public GetConnectionDetailsResult getConnectionDetails(final String uri) throws IOException { final CharSequence page = ParserUtils.scrape(uri); @@ -552,7 +548,7 @@ public class MvvProvider extends AbstractEfaProvider final String normalizedLine = normalizeLine(product, line); - parts.add(new Connection.Trip(normalizedLine, LINES.get(normalizedLine), destination, departureTime, null, 0, departure, + parts.add(new Connection.Trip(normalizedLine, LINES.get(normalizedLine), 0, destination, departureTime, null, 0, departure, arrivalTime, null, 0, arrival)); if (firstDepartureTime == null) @@ -620,6 +616,12 @@ public class MvvProvider extends AbstractEfaProvider return time; } + @Override + protected String commandLink(final String sessionId, final String command) + { + return null; + } + public String departuresQueryUri(final String stationId, final int maxDepartures) { final StringBuilder uri = new StringBuilder(); diff --git a/src/de/schildbach/pte/OebbProvider.java b/src/de/schildbach/pte/OebbProvider.java index d2cc6dbe..761d8f40 100644 --- a/src/de/schildbach/pte/OebbProvider.java +++ b/src/de/schildbach/pte/OebbProvider.java @@ -247,9 +247,9 @@ public class OebbProvider extends AbstractHafasProvider throw new SessionExpiredException(); } - List fromAddresses = null; - List viaAddresses = null; - List toAddresses = null; + List fromAddresses = null; + List viaAddresses = null; + List toAddresses = null; final Matcher mPreAddress = P_PRE_ADDRESS.matcher(page); while (mPreAddress.find()) @@ -258,12 +258,12 @@ public class OebbProvider extends AbstractHafasProvider final String options = mPreAddress.group(2); final Matcher mAddresses = P_ADDRESSES.matcher(options); - final List addresses = new ArrayList(); + final List addresses = new ArrayList(); while (mAddresses.find()) { final String address = ParserUtils.resolveEntities(mAddresses.group(1)).trim(); if (!addresses.contains(address)) - addresses.add(address); + addresses.add(new Autocomplete(LocationType.ANY, 0, address)); } if (type.equals("REQ0JourneyStopsS0K")) @@ -277,7 +277,7 @@ public class OebbProvider extends AbstractHafasProvider } if (fromAddresses != null || viaAddresses != null || toAddresses != null) - return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, fromAddresses, viaAddresses, toAddresses); + return new QueryConnectionsResult(fromAddresses, viaAddresses, toAddresses); else return queryConnections(baseUri, page); } @@ -439,7 +439,7 @@ public class OebbProvider extends AbstractHafasProvider final String arrivalPosition = mDetFine.group(12) != null ? ParserUtils.resolveEntities(mDetFine.group(12)) : null; - final Connection.Trip trip = new Connection.Trip(line, lineColors(line), null, detailsDepartureDateTime, + final Connection.Trip trip = new Connection.Trip(line, lineColors(line), 0, null, detailsDepartureDateTime, departurePosition, departureId, departure, detailsArrivalDateTime, arrivalPosition, arrivalId, arrival); connection.parts.add(trip); } diff --git a/src/de/schildbach/pte/RmvProvider.java b/src/de/schildbach/pte/RmvProvider.java index 2bf68047..df0228a8 100644 --- a/src/de/schildbach/pte/RmvProvider.java +++ b/src/de/schildbach/pte/RmvProvider.java @@ -186,9 +186,9 @@ public class RmvProvider extends AbstractHafasProvider return QueryConnectionsResult.INVALID_DATE; } - List fromAddresses = null; - List viaAddresses = null; - List toAddresses = null; + List fromAddresses = null; + List viaAddresses = null; + List toAddresses = null; final Matcher mPreAddress = P_PRE_ADDRESS.matcher(page); while (mPreAddress.find()) @@ -196,12 +196,12 @@ public class RmvProvider extends AbstractHafasProvider final String type = mPreAddress.group(1); final Matcher mAddresses = P_ADDRESSES.matcher(page); - final List addresses = new ArrayList(); + final List addresses = new ArrayList(); while (mAddresses.find()) { final String address = ParserUtils.resolveEntities(mAddresses.group(1)).trim(); if (!addresses.contains(address)) - addresses.add(address); + addresses.add(new Autocomplete(LocationType.ANY, 0, address)); } if (type == null) @@ -215,7 +215,7 @@ public class RmvProvider extends AbstractHafasProvider } if (fromAddresses != null || viaAddresses != null || toAddresses != null) - return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, fromAddresses, viaAddresses, toAddresses); + return new QueryConnectionsResult(fromAddresses, viaAddresses, toAddresses); else return queryConnections(uri, page); } @@ -362,8 +362,8 @@ public class RmvProvider extends AbstractHafasProvider final String arrivalPosition = ParserUtils.resolveEntities(mDetFine.group(6)); - lastTrip = new Connection.Trip(line, line != null ? lineColors(line) : null, destination, departureTime, departurePosition, - 0, departure, arrivalTime, arrivalPosition, 0, arrival); + lastTrip = new Connection.Trip(line, line != null ? lineColors(line) : null, 0, destination, departureTime, + departurePosition, 0, departure, arrivalTime, arrivalPosition, 0, arrival); parts.add(lastTrip); if (firstDepartureTime == null) diff --git a/src/de/schildbach/pte/SbbProvider.java b/src/de/schildbach/pte/SbbProvider.java index 0bb5425b..51c33e26 100644 --- a/src/de/schildbach/pte/SbbProvider.java +++ b/src/de/schildbach/pte/SbbProvider.java @@ -162,9 +162,9 @@ public class SbbProvider extends AbstractHafasProvider return QueryConnectionsResult.INVALID_DATE; } - List fromAddresses = null; - List viaAddresses = null; - List toAddresses = null; + List fromAddresses = null; + List viaAddresses = null; + List toAddresses = null; final Matcher mPreAddress = P_PRE_ADDRESS.matcher(page); while (mPreAddress.find()) @@ -173,12 +173,12 @@ public class SbbProvider extends AbstractHafasProvider final String options = mPreAddress.group(2); final Matcher mAddresses = P_ADDRESSES.matcher(options); - final List addresses = new ArrayList(); + final List addresses = new ArrayList(); while (mAddresses.find()) { final String address = ParserUtils.resolveEntities(mAddresses.group(1)).trim(); if (!addresses.contains(address)) - addresses.add(address); + addresses.add(new Autocomplete(LocationType.ANY, 0, address)); } if (type.equals("REQ0JourneyStopsS0K")) @@ -192,7 +192,7 @@ public class SbbProvider extends AbstractHafasProvider } if (fromAddresses != null || viaAddresses != null || toAddresses != null) - return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, fromAddresses, viaAddresses, toAddresses); + return new QueryConnectionsResult(fromAddresses, viaAddresses, toAddresses); else return queryConnections(uri, page); } @@ -340,7 +340,7 @@ public class SbbProvider extends AbstractHafasProvider final String arrivalPosition = mDetFine.group(13) != null ? ParserUtils.resolveEntities(mDetFine.group(13)) : null; - final Connection.Trip trip = new Connection.Trip(line, lineColors(line), null, departureTime, departurePosition, + final Connection.Trip trip = new Connection.Trip(line, lineColors(line), 0, null, departureTime, departurePosition, departureId, departure, arrivalTime, arrivalPosition, arrivalId, arrival); connection.parts.add(trip); } diff --git a/src/de/schildbach/pte/VbbProvider.java b/src/de/schildbach/pte/VbbProvider.java index d605c586..31c6dc59 100644 --- a/src/de/schildbach/pte/VbbProvider.java +++ b/src/de/schildbach/pte/VbbProvider.java @@ -238,12 +238,12 @@ public final class VbbProvider implements NetworkProvider final Matcher mAddress = P_CHECK_ADDRESS.matcher(page); - final List addresses = new ArrayList(); + final List addresses = new ArrayList(); while (mAddress.find()) { final String address = ParserUtils.resolveEntities(mAddress.group(1)); if (!addresses.contains(address)) - addresses.add(address); + addresses.add(new Autocomplete(LocationType.ANY, 0, address)); } if (addresses.isEmpty()) @@ -253,13 +253,13 @@ public final class VbbProvider implements NetworkProvider else if (P_CHECK_FROM.matcher(page).find()) { if (P_CHECK_TO.matcher(page).find()) - return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, null, addresses, null); + return new QueryConnectionsResult(null, addresses, null); else - return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, null, null, addresses); + return new QueryConnectionsResult(null, null, addresses); } else { - return new QueryConnectionsResult(QueryConnectionsResult.Status.AMBIGUOUS, addresses, null, null); + return new QueryConnectionsResult(addresses, null, null); } } @@ -418,7 +418,7 @@ public final class VbbProvider implements NetworkProvider final String arrival = ParserUtils.resolveEntities(mDetFine.group(10)); - parts.add(new Connection.Trip(line, line != null ? LINES.get(line) : null, destination, departureTime, departurePosition, + parts.add(new Connection.Trip(line, line != null ? LINES.get(line) : null, 0, destination, departureTime, departurePosition, departureId, departure, arrivalTime, arrivalPosition, arrivalId, arrival)); if (firstDepartureTime == null) diff --git a/src/de/schildbach/pte/VrnProvider.java b/src/de/schildbach/pte/VrnProvider.java index e435e76b..3135f31b 100644 --- a/src/de/schildbach/pte/VrnProvider.java +++ b/src/de/schildbach/pte/VrnProvider.java @@ -16,11 +16,10 @@ */ package de.schildbach.pte; -import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Date; -import de.schildbach.pte.dto.GetConnectionDetailsResult; -import de.schildbach.pte.dto.QueryConnectionsResult; import de.schildbach.pte.util.ParserUtils; /** @@ -34,7 +33,8 @@ public class VrnProvider extends AbstractEfaProvider public boolean hasCapabilities(final Capability... capabilities) { for (final Capability capability : capabilities) - if (capability == Capability.DEPARTURES) + if (capability == Capability.DEPARTURES || capability == Capability.CONNECTIONS || capability == Capability.LOCATION_STATION_ID + || capability == Capability.LOCATION_WGS84) return true; return false; @@ -77,19 +77,87 @@ public class VrnProvider extends AbstractEfaProvider return uri.toString(); } - public QueryConnectionsResult queryConnections(LocationType fromType, String from, LocationType viaType, String via, LocationType toType, - String to, Date date, boolean dep, String products, WalkSpeed walkSpeed) throws IOException + @Override + protected String connectionsQueryUri(final LocationType fromType, final String from, final LocationType viaType, final String via, + final LocationType toType, final String to, final Date date, final boolean dep, final String products, final WalkSpeed walkSpeed) { - throw new UnsupportedOperationException(); + final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); + final DateFormat TIME_FORMAT = new SimpleDateFormat("HHmm"); + + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE); + uri.append("XSLT_TRIP_REQUEST2"); + + uri.append("?language=de"); + uri.append("&outputFormat=XML"); + uri.append("&coordOutputFormat=WGS84"); + + if (fromType == LocationType.WGS84) + { + final String[] parts = from.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_origin=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_origin=coord"); + } + else + { + uri.append("&type_origin=").append(locationTypeValue(fromType)); + uri.append("&name_origin=").append(ParserUtils.urlEncode(from, "ISO-8859-1")); // fine-grained location + } + + if (toType == LocationType.WGS84) + { + final String[] parts = to.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_destination=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_destination=coord"); + } + else + { + uri.append("&type_destination=").append(locationTypeValue(toType)); + uri.append("&name_destination=").append(ParserUtils.urlEncode(to, "ISO-8859-1")); // fine-grained location + } + + if (via != null) + { + if (viaType == LocationType.WGS84) + { + final String[] parts = via.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_via=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_via=coord"); + } + else + { + uri.append("&type_via=").append(locationTypeValue(viaType)); + uri.append("&name_via=").append(ParserUtils.urlEncode(via, "ISO-8859-1")); + } + } + + uri.append("&itdDate=").append(ParserUtils.urlEncode(DATE_FORMAT.format(date))); + uri.append("&itdTime=").append(ParserUtils.urlEncode(TIME_FORMAT.format(date))); + uri.append("&itdTripDateTimeDepArr=").append(dep ? "dep" : "arr"); + + // TODO products + + uri.append("&changeSpeed=").append(WALKSPEED_MAP.get(walkSpeed)); + + uri.append("&locationServerActive=1"); + + return uri.toString(); } - public QueryConnectionsResult queryMoreConnections(String uri) throws IOException + @Override + protected String commandLink(final String sessionId, final String command) { - throw new UnsupportedOperationException(); - } - - public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException - { - throw new UnsupportedOperationException(); + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE); + uri.append("XSLT_TRIP_REQUEST2"); + uri.append("?sessionID=").append(sessionId); + uri.append("&command=").append(command); + return uri.toString(); } } diff --git a/src/de/schildbach/pte/VrrProvider.java b/src/de/schildbach/pte/VrrProvider.java index 3f6e152f..eafcab89 100644 --- a/src/de/schildbach/pte/VrrProvider.java +++ b/src/de/schildbach/pte/VrrProvider.java @@ -16,11 +16,10 @@ */ package de.schildbach.pte; -import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.Date; -import de.schildbach.pte.dto.GetConnectionDetailsResult; -import de.schildbach.pte.dto.QueryConnectionsResult; import de.schildbach.pte.util.ParserUtils; /** @@ -34,7 +33,8 @@ public class VrrProvider extends AbstractEfaProvider public boolean hasCapabilities(final Capability... capabilities) { for (final Capability capability : capabilities) - if (capability == Capability.DEPARTURES) + if (capability == Capability.DEPARTURES || capability == Capability.CONNECTIONS || capability == Capability.LOCATION_STATION_ID + || capability == Capability.LOCATION_WGS84) return true; return false; @@ -76,19 +76,87 @@ public class VrrProvider extends AbstractEfaProvider return uri.toString(); } - public QueryConnectionsResult queryConnections(LocationType fromType, String from, LocationType viaType, String via, LocationType toType, - String to, Date date, boolean dep, String products, WalkSpeed walkSpeed) throws IOException + @Override + protected String connectionsQueryUri(final LocationType fromType, final String from, final LocationType viaType, final String via, + final LocationType toType, final String to, final Date date, final boolean dep, final String products, final WalkSpeed walkSpeed) { - throw new UnsupportedOperationException(); + final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd"); + final DateFormat TIME_FORMAT = new SimpleDateFormat("HHmm"); + + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE); + uri.append("XSLT_TRIP_REQUEST2"); + + uri.append("?language=de"); + uri.append("&outputFormat=XML"); + uri.append("&coordOutputFormat=WGS84"); + + if (fromType == LocationType.WGS84) + { + final String[] parts = from.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_origin=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_origin=coord"); + } + else + { + uri.append("&type_origin=").append(locationTypeValue(fromType)); + uri.append("&name_origin=").append(ParserUtils.urlEncode(from, "ISO-8859-1")); // fine-grained location + } + + if (toType == LocationType.WGS84) + { + final String[] parts = to.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_destination=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_destination=coord"); + } + else + { + uri.append("&type_destination=").append(locationTypeValue(toType)); + uri.append("&name_destination=").append(ParserUtils.urlEncode(to, "ISO-8859-1")); // fine-grained location + } + + if (via != null) + { + if (viaType == LocationType.WGS84) + { + final String[] parts = via.split(",\\s*", 2); + final int lat = Integer.parseInt(parts[0]); + final int lon = Integer.parseInt(parts[1]); + uri.append("&nameInfo_via=").append(String.format("%2.6f:%2.6f", lon / 1E6, lat / 1E6)).append(":WGS84[DD.dddddd]"); + uri.append("&typeInfo_via=coord"); + } + else + { + uri.append("&type_via=").append(locationTypeValue(viaType)); + uri.append("&name_via=").append(ParserUtils.urlEncode(via, "ISO-8859-1")); + } + } + + uri.append("&itdDate=").append(ParserUtils.urlEncode(DATE_FORMAT.format(date))); + uri.append("&itdTime=").append(ParserUtils.urlEncode(TIME_FORMAT.format(date))); + uri.append("&itdTripDateTimeDepArr=").append(dep ? "dep" : "arr"); + + // TODO products + + uri.append("&changeSpeed=").append(WALKSPEED_MAP.get(walkSpeed)); + + uri.append("&locationServerActive=1"); + + return uri.toString(); } - public QueryConnectionsResult queryMoreConnections(String uri) throws IOException + @Override + protected String commandLink(final String sessionId, final String command) { - throw new UnsupportedOperationException(); - } - - public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException - { - throw new UnsupportedOperationException(); + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE); + uri.append("XSLT_TRIP_REQUEST2"); + uri.append("?sessionID=").append(sessionId); + uri.append("&command=").append(command); + return uri.toString(); } } diff --git a/src/de/schildbach/pte/dto/Connection.java b/src/de/schildbach/pte/dto/Connection.java index ee94cb4d..a74d1857 100644 --- a/src/de/schildbach/pte/dto/Connection.java +++ b/src/de/schildbach/pte/dto/Connection.java @@ -91,6 +91,7 @@ public final class Connection implements Serializable { final public String line; final public int[] lineColors; + final public int destinationId; final public String destination; final public Date departureTime; final public String departurePosition; @@ -101,12 +102,13 @@ public final class Connection implements Serializable final public int arrivalId; final public String arrival; - public Trip(final String line, final int[] lineColors, final String destination, final Date departureTime, final String departurePosition, - final int departureId, final String departure, final Date arrivalTime, final String arrivalPosition, final int arrivalId, - final String arrival) + public Trip(final String line, final int[] lineColors, final int destinationId, final String destination, final Date departureTime, + final String departurePosition, final int departureId, final String departure, final Date arrivalTime, final String arrivalPosition, + final int arrivalId, final String arrival) { this.line = line; this.lineColors = lineColors; + this.destinationId = destinationId; this.destination = destination; this.departureTime = departureTime; this.departurePosition = departurePosition; @@ -124,7 +126,7 @@ public final class Connection implements Serializable final StringBuilder builder = new StringBuilder(getClass().getName() + "["); builder.append("line=").append(line); builder.append(","); - builder.append("destination=").append(destination); + builder.append("destination=").append(destination).append("/").append(destinationId); builder.append(","); builder.append("departure=").append(departureTime).append("/").append(departurePosition).append("/").append(departureId).append("/") .append(departure); diff --git a/src/de/schildbach/pte/dto/QueryConnectionsResult.java b/src/de/schildbach/pte/dto/QueryConnectionsResult.java index 8e1a1b19..d938cb73 100644 --- a/src/de/schildbach/pte/dto/QueryConnectionsResult.java +++ b/src/de/schildbach/pte/dto/QueryConnectionsResult.java @@ -31,15 +31,15 @@ public final class QueryConnectionsResult implements Serializable OK, AMBIGUOUS, TOO_CLOSE, NO_CONNECTIONS, INVALID_DATE; } - public static final QueryConnectionsResult TOO_CLOSE = new QueryConnectionsResult(Status.TOO_CLOSE, null, null, null); - public static final QueryConnectionsResult NO_CONNECTIONS = new QueryConnectionsResult(Status.NO_CONNECTIONS, null, null, null); - public static final QueryConnectionsResult INVALID_DATE = new QueryConnectionsResult(Status.INVALID_DATE, null, null, null); + public static final QueryConnectionsResult TOO_CLOSE = new QueryConnectionsResult(Status.TOO_CLOSE); + public static final QueryConnectionsResult NO_CONNECTIONS = new QueryConnectionsResult(Status.NO_CONNECTIONS); + public static final QueryConnectionsResult INVALID_DATE = new QueryConnectionsResult(Status.INVALID_DATE); public final Status status; - public final List ambiguousFromAddresses; - public final List ambiguousViaAddresses; - public final List ambiguousToAddresses; + public final List ambiguousFrom; + public final List ambiguousVia; + public final List ambiguousTo; public final String queryUri; public final String from; @@ -49,23 +49,6 @@ public final class QueryConnectionsResult implements Serializable public final String linkLater; public final List connections; - public QueryConnectionsResult(final Status status, final List ambiguousFromAddresses, final List ambiguousViaAddresses, - final List ambiguousToAddresses) - { - this.status = status; - this.ambiguousFromAddresses = ambiguousFromAddresses; - this.ambiguousViaAddresses = ambiguousViaAddresses; - this.ambiguousToAddresses = ambiguousToAddresses; - - this.queryUri = null; - this.from = null; - this.to = null; - this.currentDate = null; - this.linkEarlier = null; - this.linkLater = null; - this.connections = null; - } - public QueryConnectionsResult(final String queryUri, final String from, final String to, final Date currentDate, final String linkEarlier, final String linkLater, final List connections) { @@ -78,9 +61,41 @@ public final class QueryConnectionsResult implements Serializable this.linkLater = linkLater; this.connections = connections; - this.ambiguousFromAddresses = null; - this.ambiguousViaAddresses = null; - this.ambiguousToAddresses = null; + this.ambiguousFrom = null; + this.ambiguousVia = null; + this.ambiguousTo = null; + } + + public QueryConnectionsResult(final List ambiguousFrom, final List ambiguousVia, final List ambiguousTo) + { + this.status = Status.AMBIGUOUS; + this.ambiguousFrom = ambiguousFrom; + this.ambiguousVia = ambiguousVia; + this.ambiguousTo = ambiguousTo; + + this.queryUri = null; + this.from = null; + this.to = null; + this.currentDate = null; + this.linkEarlier = null; + this.linkLater = null; + this.connections = null; + } + + public QueryConnectionsResult(final Status status) + { + this.status = status; + + this.ambiguousFrom = null; + this.ambiguousVia = null; + this.ambiguousTo = null; + this.queryUri = null; + this.from = null; + this.to = null; + this.currentDate = null; + this.linkEarlier = null; + this.linkLater = null; + this.connections = null; } @Override @@ -90,12 +105,12 @@ public final class QueryConnectionsResult implements Serializable builder.append("[").append(this.status).append(": "); if (connections != null) builder.append(connections.size()).append(" connections, "); - if (ambiguousFromAddresses != null) - builder.append(ambiguousFromAddresses.size()).append(" ambiguous fromAddresses, "); - if (ambiguousViaAddresses != null) - builder.append(ambiguousViaAddresses.size()).append(" ambiguous viaAddresses, "); - if (ambiguousToAddresses != null) - builder.append(ambiguousToAddresses.size()).append(" ambiguous toAddresses, "); + if (ambiguousFrom != null) + builder.append(ambiguousFrom.size()).append(" ambiguous from, "); + if (ambiguousVia != null) + builder.append(ambiguousVia.size()).append(" ambiguous via, "); + if (ambiguousTo != null) + builder.append(ambiguousTo.size()).append(" ambiguous to, "); if (builder.substring(builder.length() - 2).equals(", ")) builder.setLength(builder.length() - 2); builder.append("]"); diff --git a/test/de/schildbach/pte/live/GvhProviderLiveTest.java b/test/de/schildbach/pte/live/GvhProviderLiveTest.java index 2814ddf8..8e82ac49 100644 --- a/test/de/schildbach/pte/live/GvhProviderLiveTest.java +++ b/test/de/schildbach/pte/live/GvhProviderLiveTest.java @@ -16,13 +16,17 @@ */ package de.schildbach.pte.live; +import java.util.Date; import java.util.List; import org.junit.Test; import de.schildbach.pte.GvhProvider; +import de.schildbach.pte.NetworkProvider.LocationType; +import de.schildbach.pte.NetworkProvider.WalkSpeed; import de.schildbach.pte.dto.Autocomplete; import de.schildbach.pte.dto.NearbyStationsResult; +import de.schildbach.pte.dto.QueryConnectionsResult; /** * @author Andreas Schildbach @@ -30,6 +34,7 @@ import de.schildbach.pte.dto.NearbyStationsResult; public class GvhProviderLiveTest { private final GvhProvider provider = new GvhProvider(); + private static final String ALL_PRODUCTS = "IRSUTBFC"; @Test public void autocompleteIncomplete() throws Exception @@ -78,4 +83,12 @@ public class GvhProviderLiveTest System.out.println(result.stations.size() + " " + result.stations); } + + @Test + public void incompleteConnection() throws Exception + { + final QueryConnectionsResult result = provider.queryConnections(LocationType.ANY, "hann", null, null, LocationType.ANY, "laat", new Date(), + true, ALL_PRODUCTS, WalkSpeed.FAST); + System.out.println(result); + } } diff --git a/test/de/schildbach/pte/live/LinzProviderLiveTest.java b/test/de/schildbach/pte/live/LinzProviderLiveTest.java index 5e226891..d694edb5 100644 --- a/test/de/schildbach/pte/live/LinzProviderLiveTest.java +++ b/test/de/schildbach/pte/live/LinzProviderLiveTest.java @@ -76,14 +76,22 @@ public class LinzProviderLiveTest System.out.println(result); } + @Test + public void incompleteConnection() throws Exception + { + final QueryConnectionsResult result = provider.queryConnections(LocationType.ANY, "linz", null, null, LocationType.ANY, "gel", new Date(), + true, ALL_PRODUCTS, WalkSpeed.FAST); + System.out.println(result); + } + @Test public void shortConnection() throws Exception { final QueryConnectionsResult result = provider.queryConnections(LocationType.STATION, "Linz Hauptbahnhof", null, null, LocationType.STATION, "Linz Auwiesen", new Date(), true, ALL_PRODUCTS, WalkSpeed.FAST); System.out.println(result); - // final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); - // System.out.println(moreResult); + final QueryConnectionsResult moreResult = provider.queryMoreConnections(result.linkLater); + System.out.println(moreResult); } @Test