From f8ebbc4054af01065c4c30f6b3d3bb79bb0b23f6 Mon Sep 17 00:00:00 2001 From: "andreas.schildbach" Date: Thu, 23 Sep 2010 11:14:46 +0000 Subject: [PATCH] switch Austrian departures to JSON git-svn-id: https://public-transport-enabler.googlecode.com/svn/trunk@181 0924bc21-9374-b0fa-ee44-9ff1593b38f0 --- pom.xml | 58 +++ src/de/schildbach/pte/OebbProvider.java | 478 +++++++++---------- test/de/schildbach/pte/OebbProviderTest.java | 53 -- 3 files changed, 280 insertions(+), 309 deletions(-) create mode 100644 pom.xml delete mode 100644 test/de/schildbach/pte/OebbProviderTest.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..12179e1c --- /dev/null +++ b/pom.xml @@ -0,0 +1,58 @@ + + 4.0.0 + + de.schildbach.pte + public-transport-enabler + 1.0-SNAPSHOT + + + + + org.json + json + 20090211 + provided + + + + junit + junit + 4.4 + test + + + + + + src + test + + + src + + **/*.java + + + + + + test + + **/*.java + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.5 + 1.5 + + + + + + diff --git a/src/de/schildbach/pte/OebbProvider.java b/src/de/schildbach/pte/OebbProvider.java index d297e016..0e57939b 100644 --- a/src/de/schildbach/pte/OebbProvider.java +++ b/src/de/schildbach/pte/OebbProvider.java @@ -4,27 +4,27 @@ import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Calendar; import java.util.Date; -import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import de.schildbach.pte.QueryDeparturesResult.Status; public class OebbProvider implements NetworkProvider { public static final String NETWORK_ID = "fahrplan.oebb.at"; - private static final long PARSER_DAY_ROLLOVER_THRESHOLD_MS = 12 * 60 * 60 * 1000; - public boolean hasCapabilities(final Capability... capabilities) { for (final Capability capability : capabilities) - if (capability == Capability.DEPARTURES || capability == Capability.CONNECTIONS || capability == Capability.LOCATION_STATION_ID) + if (capability == Capability.DEPARTURES || capability == Capability.LOCATION_STATION_ID) return true; return false; @@ -462,142 +462,99 @@ public class OebbProvider implements NetworkProvider public String departuresQueryUri(final String stationId, final int maxDepartures) { + final DateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm"); + final Date now = new Date(); + final StringBuilder uri = new StringBuilder(); - uri.append("http://fahrplan.oebb.at/bin/stboard.exe/dn"); - uri.append("?input=").append(stationId); + + uri.append("http://fahrplan.oebb.at/bin/stboard.exe/dn?L=vs_scotty.vs_stb"); + uri.append("&input=").append(stationId); uri.append("&boardType=dep"); + uri.append("&time=").append(TIME_FORMAT.format(now)); uri.append("&productsFilter=111111111111"); - if (maxDepartures != 0) - uri.append("&maxJourneys=").append(maxDepartures); - uri.append("&disableEquivs=yes"); // don't use nearby stations + uri.append("&additionalTime=0"); + uri.append("&maxJourneys=20"); uri.append("&start=yes"); + uri.append("&selectDate=today"); + uri.append("&monitor=1"); + uri.append("&requestType=0"); + uri.append("&view=preview"); + uri.append("&disableEquivs=yes"); // don't use nearby stations + return uri.toString(); } - private static final Pattern P_DEPARTURES_HEAD_COARSE = Pattern.compile(".*?" // - + "(?:" // - + "]*>(.+?)
.*?" // - + "(?:]*>(.+?)
|(verkehren an dieser Haltestelle keine))"// - + "|(Eingabe kann nicht interpretiert)|(Verbindung zum Server konnte leider nicht hergestellt werden))" // - + ".*?" // - , Pattern.DOTALL); - private static final Pattern P_DEPARTURES_HEAD_FINE = Pattern.compile(".*?" // - + "\\s*(.*?)\\s*\n(.*?)", Pattern.DOTALL); - static final Pattern P_DEPARTURES_FINE = Pattern.compile("" // - + "(\\d{1,2}:\\d{2})\n.*?" // plannedTime - + "(?:\n" // - + "(?: |(pünktlich|\\d{1,2}:\\d{2}))\n\n" // predictedTime - + ")?.*?" // - + "\\s*(.*?)\\s*\n" // - + "" // destinationId - + "\\s*(.*?)\\s*\n" // destination - + ".*?" // - + "(?:\n(" + ParserUtils.P_PLATFORM + ").*?)?" // position - , Pattern.DOTALL); - public QueryDeparturesResult queryDepartures(final String uri) throws IOException { // scrape page - final CharSequence page = ParserUtils.scrape(uri); + final String page = ParserUtils.scrape(uri).toString().substring(14); - // parse page - final Matcher mHeadCoarse = P_DEPARTURES_HEAD_COARSE.matcher(page); - if (mHeadCoarse.matches()) + try { - // messages - if (mHeadCoarse.group(3) != null) - return new QueryDeparturesResult(uri, Status.NO_INFO); - else if (mHeadCoarse.group(4) != null) + final JSONObject head = new JSONObject(page); + final String location = ParserUtils.resolveEntities(head.getString("stationName")); + final int locationId = head.optInt("stationEvaId", -1); + // final boolean rt = head.optBoolean("rtInfo"); + if (locationId == -1) return new QueryDeparturesResult(uri, Status.INVALID_STATION); - else if (mHeadCoarse.group(5) != null) - return new QueryDeparturesResult(uri, Status.SERVICE_DOWN); - final Matcher mHeadFine = P_DEPARTURES_HEAD_FINE.matcher(mHeadCoarse.group(1)); - if (mHeadFine.matches()) + final List departures = new ArrayList(8); + + final JSONArray aDeparture = head.optJSONArray("journey"); + if (aDeparture != null) { - final String location = ParserUtils.resolveEntities(mHeadFine.group(1)); - final Date currentTime = ParserUtils.joinDateTime(ParserUtils.parseDate(mHeadFine.group(2)), ParserUtils - .parseTime(mHeadFine.group(3))); - final int stationId = Integer.parseInt(mHeadFine.group(4)); - final List departures = new ArrayList(8); - String oldZebra = null; - - final Matcher mDepCoarse = P_DEPARTURES_COARSE.matcher(mHeadCoarse.group(2)); - while (mDepCoarse.find()) + for (int i = 0; i < aDeparture.length(); i++) { - final String zebra = mDepCoarse.group(1); - if (oldZebra != null && zebra.equals(oldZebra)) - throw new IllegalArgumentException("missed row? last:" + zebra); - else - oldZebra = zebra; - - final Matcher mDepFine = P_DEPARTURES_FINE.matcher(mDepCoarse.group(2)); - if (mDepFine.matches()) + final JSONObject departure = aDeparture.optJSONObject(i); + if (departure != null) { - final Calendar current = new GregorianCalendar(); - current.setTime(currentTime); - final Calendar parsed = new GregorianCalendar(); - parsed.setTime(ParserUtils.parseTime(mDepFine.group(1))); - parsed.set(Calendar.YEAR, current.get(Calendar.YEAR)); - parsed.set(Calendar.MONTH, current.get(Calendar.MONTH)); - parsed.set(Calendar.DAY_OF_MONTH, current.get(Calendar.DAY_OF_MONTH)); - if (ParserUtils.timeDiff(parsed.getTime(), currentTime) < -PARSER_DAY_ROLLOVER_THRESHOLD_MS) - parsed.add(Calendar.DAY_OF_MONTH, 1); + final Date time = ParserUtils.joinDateTime(ParserUtils.parseDate(departure.getString("da")), ParserUtils.parseTime(departure + .getString("ti"))); + final String line = normalizeLine(departure.getString("pr")); + final String destination = ParserUtils.resolveEntities(departure.getString("st")); + String position = departure.optString("tr"); + if (position != null) + position = "Gl. " + position; + final boolean rt = head.optBoolean("rt", false); + if (rt == true) + System.out.println("Realtime!!"); + final String lineLink = departure.optString("tinfoline"); - final Date plannedTime = parsed.getTime(); - - Date predictedTime = null; - final String prognosis = ParserUtils.resolveEntities(mDepFine.group(2)); - if (prognosis != null) - { - if (prognosis.equals("pünktlich")) - predictedTime = plannedTime; - else - predictedTime = ParserUtils.joinDateTime(currentTime, ParserUtils.parseTime(prognosis)); - } - - final String lineType = mDepFine.group(3); - - final String line = normalizeLine(lineType, ParserUtils.resolveEntities(mDepFine.group(4))); - - final int destinationId = mDepFine.group(5) != null ? Integer.parseInt(mDepFine.group(5)) : 0; - - final String destination = ParserUtils.resolveEntities(mDepFine.group(6)); - - final String position = mDepFine.group(7) != null ? "Gl. " + ParserUtils.resolveEntities(mDepFine.group(7)) : null; - - final Departure dep = new Departure(plannedTime, predictedTime, line, line != null ? LINES.get(line.charAt(0)) : null, null, - position, destinationId, destination, null); - - if (!departures.contains(dep)) - departures.add(dep); - } - else - { - throw new IllegalArgumentException("cannot parse '" + mDepCoarse.group(2) + "' on " + uri); + departures.add(new Departure(!rt ? time : null, rt ? time : null, line, line != null ? LINES.get(line.charAt(0)) : null, + lineLink, position, 0, destination, null)); } } + } - return new QueryDeparturesResult(uri, stationId, location, currentTime, departures); - } - else - { - throw new IllegalArgumentException("cannot parse '" + mHeadCoarse.group(1) + "' on " + uri); - } + return new QueryDeparturesResult(uri, locationId, location, null, departures); } - else + catch (final JSONException x) { - throw new IllegalArgumentException("cannot parse '" + page + "' on " + uri); + throw new RuntimeException("cannot parse: '" + page + "' on " + uri, x); } } - private static final Pattern P_NORMALIZE_LINE = Pattern.compile("([A-Za-zÄÖÜäöüßáàâéèêíìîóòôúùû]+)[\\s-]*(.*)"); + private static final Pattern P_NORMALIZE_LINE = Pattern.compile("([A-Za-zÄÖÜäöüßáàâéèêíìîóòôúùû-]+)[\\s]*(.*)"); + + private static String normalizeLine(final String line) + { + final Matcher m = P_NORMALIZE_LINE.matcher(line); + if (m.matches()) + { + final String type = m.group(1); + final String number = m.group(2); + + final char normalizedType = normalizeType(type); + if (normalizedType != 0) + return normalizedType + type + number; + + throw new IllegalStateException("cannot normalize type " + type + " line " + line); + } + else + { + throw new IllegalStateException("cannot normalize line " + line); + } + } private static String normalizeLine(final String type, final String line) { @@ -608,7 +565,8 @@ public class OebbProvider implements NetworkProvider if (normalizedType != 0) return normalizedType + strippedLine; - throw new IllegalStateException("cannot normalize type " + type + " line " + line); + return '?' + strippedLine; + // throw new IllegalStateException("cannot normalize type " + type + " line " + line); } private static char normalizeType(final String type) @@ -625,176 +583,184 @@ public class OebbProvider implements NetworkProvider return 'I'; if (ucType.equals("ICE")) // InterCityExpress return 'I'; - if (ucType.equals("X")) // Interconnex - return 'I'; - if (ucType.equals("EN")) // EuroNight - return 'I'; - if (ucType.equals("CNL")) // CityNightLine - return 'I'; - if (ucType.equals("DNZ")) // Berlin-Saratov, Berlin-Moskva - return 'I'; - if (ucType.equals("INT")) // Rußland - return 'I'; - if (ucType.equals("D")) // Rußland - return 'I'; - if (ucType.equals("RR")) // Finnland - return 'I'; - if (ucType.equals("TLK")) // Tanie Linie Kolejowe, Polen - return 'I'; - if (ucType.equals("EE")) // Rumänien - return 'I'; - if (ucType.equals("SC")) // SuperCity, Tschechien - return 'I'; - if (ucType.equals("RJ")) // RailJet, Österreichische Bundesbahnen - return 'I'; - if (ucType.equals("EST")) // Eurostar Frankreich - return 'I'; - if (ucType.equals("ALS")) // Spanien - return 'I'; - if (ucType.equals("ARC")) // Spanien - return 'I'; - if (ucType.equals("TLG")) // Spanien, Madrid - return 'I'; - if (ucType.equals("HOT")) // Spanien, Nacht - return 'I'; - if (ucType.equals("AVE")) // Alta Velocidad Española, Spanien - return 'I'; - if (ucType.equals("INZ")) // Schweden, Nacht - return 'I'; - if (ucType.equals("OZ")) // Schweden, Oeresundzug - return 'I'; - if (ucType.equals("X2")) // Schweden - return 'I'; - if (ucType.equals("THA")) // Thalys - return 'I'; - if (ucType.equals("TGV")) // Train à Grande Vitesse - return 'I'; - if (ucType.equals("LYN")) // Dänemark - return 'I'; + // if (ucType.equals("X")) // Interconnex + // return 'I'; + // if (ucType.equals("EN")) // EuroNight + // return 'I'; + // if (ucType.equals("CNL")) // CityNightLine + // return 'I'; + // if (ucType.equals("DNZ")) // Berlin-Saratov, Berlin-Moskva + // return 'I'; + // if (ucType.equals("INT")) // Rußland + // return 'I'; + // if (ucType.equals("D")) // Rußland + // return 'I'; + // if (ucType.equals("RR")) // Finnland + // return 'I'; + // if (ucType.equals("TLK")) // Tanie Linie Kolejowe, Polen + // return 'I'; + // if (ucType.equals("EE")) // Rumänien + // return 'I'; + // if (ucType.equals("SC")) // SuperCity, Tschechien + // return 'I'; + // if (ucType.equals("RJ")) // RailJet, Österreichische Bundesbahnen + // return 'I'; + // if (ucType.equals("EST")) // Eurostar Frankreich + // return 'I'; + // if (ucType.equals("ALS")) // Spanien + // return 'I'; + // if (ucType.equals("ARC")) // Spanien + // return 'I'; + // if (ucType.equals("TLG")) // Spanien, Madrid + // return 'I'; + // if (ucType.equals("HOT")) // Spanien, Nacht + // return 'I'; + // if (ucType.equals("AVE")) // Alta Velocidad Española, Spanien + // return 'I'; + // if (ucType.equals("INZ")) // Schweden, Nacht + // return 'I'; + // if (ucType.equals("OZ")) // Schweden, Oeresundzug + // return 'I'; + // if (ucType.equals("X2")) // Schweden + // return 'I'; + // if (ucType.equals("THA")) // Thalys + // return 'I'; + // if (ucType.equals("TGV")) // Train à Grande Vitesse + // return 'I'; + // if (ucType.equals("LYN")) // Dänemark + // return 'I'; if (ucType.equals("ARZ")) // Frankreich, Nacht return 'I'; - if (ucType.equals("ES")) // Eurostar Italia - return 'I'; - if (ucType.equals("ICN")) // Italien, Nacht - return 'I'; - if (ucType.equals("UUU")) // Italien, Nacht - return 'I'; - if (ucType.equals("RHI")) // ICE - return 'I'; - if (ucType.equals("RHT")) // TGV - return 'I'; - if (ucType.equals("TGD")) // TGV - return 'I'; - if (ucType.equals("ECB")) // EC - return 'I'; - if (ucType.equals("IRX")) // IC - return 'I'; - if (ucType.equals("AIR")) - return 'I'; - + // if (ucType.equals("ES")) // Eurostar Italia + // return 'I'; + // if (ucType.equals("ICN")) // Italien, Nacht + // return 'I'; + // if (ucType.equals("UUU")) // Italien, Nacht + // return 'I'; + // if (ucType.equals("RHI")) // ICE + // return 'I'; + // if (ucType.equals("RHT")) // TGV + // return 'I'; + // if (ucType.equals("TGD")) // TGV + // return 'I'; + // if (ucType.equals("ECB")) // EC + // return 'I'; + // if (ucType.equals("IRX")) // IC + // return 'I'; + // if (ucType.equals("AIR")) + // return 'I'; + // if (ucType.equals("R")) return 'R'; if (ucType.equals("REX")) // RegionalExpress return 'R'; - if (ucType.equals("ZUG")) + // if (ucType.equals("ZUG")) + // return 'R'; + // if (ucType.equals("EZ")) // Erlebniszug + // return 'R'; + // if (ucType.equals("S2")) // Helsinki-Turku + // return 'R'; + if (ucType.equals("RB")) // RegionalBahn Deutschland return 'R'; - if (ucType.equals("EZ")) // Erlebniszug - return 'R'; - if (ucType.equals("S2")) // Helsinki-Turku - return 'R'; - if (ucType.equals("RB")) // RegionalBahn - return 'R'; - if (ucType.equals("RE")) - return 'R'; - if (ucType.equals("DPN")) // TODO nicht evtl. doch eher ne S-Bahn? - return 'R'; - if (ucType.equals("VIA")) - return 'R'; - if (ucType.equals("PCC")) // Polen - return 'R'; - if (ucType.equals("KM")) // Polen - return 'R'; - if (ucType.equals("SKM")) // Polen - return 'R'; - if (ucType.equals("SKW")) // Polen - return 'R'; - if (ucType.equals("WKD")) // Warszawska Kolej Dojazdowa, Polen - return 'R'; - if (ucType.equals("IR")) // Polen - return 'R'; - if (ucType.equals("OS")) // Chop-Cierna nas Tisou - return 'R'; - if (ucType.equals("SP")) // Polen - return 'R'; - if (ucType.equals("EX")) // Polen - return 'R'; - if (ucType.equals("E")) // Budapest, Ungarn - return 'R'; - if (ucType.equals("IP")) // Ozd, Ungarn - return 'R'; - if (ucType.equals("ZR")) // Bratislava, Slovakai - return 'R'; - if (ucType.equals("CAT")) // Stockholm-Arlanda, Arlanda Express - return 'R'; - if (ucType.equals("RT")) // Deutschland - return 'R'; - if (ucType.equals("IRE")) // Interregio Express - return 'R'; - if (ucType.equals("N")) // Frankreich, Tours - return 'R'; - if (ucType.equals("DPF")) // VX=Vogtland Express + if (ucType.equals("RE")) // RegionalExpress Deutschland return 'R'; + // if (ucType.equals("DPN")) // TODO nicht evtl. doch eher ne S-Bahn? + // return 'R'; + // if (ucType.equals("VIA")) + // return 'R'; + // if (ucType.equals("PCC")) // Polen + // return 'R'; + // if (ucType.equals("KM")) // Polen + // return 'R'; + // if (ucType.equals("SKM")) // Polen + // return 'R'; + // if (ucType.equals("SKW")) // Polen + // return 'R'; + // if (ucType.equals("WKD")) // Warszawska Kolej Dojazdowa, Polen + // return 'R'; + // if (ucType.equals("IR")) // Polen + // return 'R'; + // if (ucType.equals("OS")) // Chop-Cierna nas Tisou + // return 'R'; + // if (ucType.equals("SP")) // Polen + // return 'R'; + // if (ucType.equals("EX")) // Polen + // return 'R'; + // if (ucType.equals("E")) // Budapest, Ungarn + // return 'R'; + // if (ucType.equals("IP")) // Ozd, Ungarn + // return 'R'; + // if (ucType.equals("ZR")) // Bratislava, Slovakai + // return 'R'; + // if (ucType.equals("CAT")) // Stockholm-Arlanda, Arlanda Express + // return 'R'; + // if (ucType.equals("RT")) // Deutschland + // return 'R'; + // if (ucType.equals("IRE")) // Interregio Express + // return 'R'; + // if (ucType.equals("N")) // Frankreich, Tours + // return 'R'; + // if (ucType.equals("DPF")) // VX=Vogtland Express + // return 'R'; if (ucType.equals("S")) return 'S'; if (ucType.equals("RSB")) // Schnellbahn Wien return 'S'; - if (ucType.equals("RER")) // Réseau Express Régional, Frankreich - return 'S'; + // if (ucType.equals("RER")) // Réseau Express Régional, Frankreich + // return 'S'; if (ucType.equals("U")) return 'U'; if (ucType.equals("STR")) return 'T'; - if (ucType.equals("LKB")) + // if (ucType.equals("LKB")) + // return 'T'; + if (ucType.equals("WLB")) // via JSON API return 'T'; if (ucType.equals("BUS")) return 'B'; - if (ucType.equals("RFB")) - return 'B'; - if (ucType.equals("OBU")) - return 'B'; + // if (ucType.equals("RFB")) + // return 'B'; + // if (ucType.equals("OBU")) + // return 'B'; if (ucType.equals("AST")) return 'B'; - if (ucType.equals("ICB")) // ICBus + if (ucType.equals("ASTSV")) // via JSON API return 'B'; - if (ucType.equals("FB")) // Polen + if (ucType.equals("ICB")) // ÖBB ICBus return 'B'; - if (ucType.equals("BSV")) // Deutschland + // if (ucType.equals("FB")) // Polen + // return 'B'; + // if (ucType.equals("BSV")) // Deutschland + // return 'B'; + // if (ucType.equals("LT")) // Linien-Taxi + // return 'B'; + if (ucType.equals("BUSSV")) // via JSON API return 'B'; - if (ucType.equals("LT")) // Linien-Taxi + if (ucType.equals("O-B")) // Stadtbus, via JSON API return 'B'; - if (ucType.equals("SCH")) - return 'F'; - if (ucType.equals("AS")) // SyltShuttle - return 'F'; - - if (ucType.equals("SB")) - return 'C'; - if (ucType.equals("LIF")) - return 'C'; - - if (ucType.equals("U70")) // U.K. - return '?'; - if (ucType.equals("R84")) - return '?'; - if (ucType.equals("S84")) - return '?'; - if (ucType.equals("T84")) - return '?'; + // if (ucType.equals("SCH")) + // return 'F'; + // if (ucType.equals("AS")) // SyltShuttle + // return 'F'; + // + // if (ucType.equals("SB")) + // return 'C'; + // if (ucType.equals("LIF")) + // return 'C'; + // + // if (ucType.equals("U70")) // U.K. + // return '?'; + // if (ucType.equals("R84")) + // return '?'; + // if (ucType.equals("S84")) + // return '?'; + // if (ucType.equals("T84")) + // return '?'; return 0; } diff --git a/test/de/schildbach/pte/OebbProviderTest.java b/test/de/schildbach/pte/OebbProviderTest.java deleted file mode 100644 index c3f3701c..00000000 --- a/test/de/schildbach/pte/OebbProviderTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package de.schildbach.pte; - -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; - -import java.util.regex.Matcher; - -import org.junit.Test; - -public class OebbProviderTest -{ - @Test - public void testDepartures() - { - assertFineDepartures("" // - + "09:12\n" // - + "\n" // - + "\"Bus Bus 16A\n" // - + "\n" // - + "\n" // - + "\n" // - + "\n" // - + "Wien Hetzendorf Bahnhst\n" // - + "\n" // - + "\n" // - + "
\n" // - + "\n" // - + "Wien Breitenfurter Straße/Hetzendorfer Straße\n" // - + "\n" // - + "09:12\n" // - + "-\n" // - + "\n" // - + "Wien Hetzendorf Bahnhst (Eckartsaugasse)\n" // - + "\n" // - + "09:13\n" // - + "\n"); - } - - private Matcher assertFineDepartures(String s) - { - Matcher m = OebbProvider.P_DEPARTURES_FINE.matcher(s); - assertTrue(m.matches()); - - // ParserUtils.printGroups(m); - - assertNotNull(m.group(1)); // time - assertNotNull(m.group(3)); // lineType - assertNotNull(m.group(4)); // line - assertNotNull(m.group(6)); // destination - - return m; - } -}