switch Austrian departures to JSON

git-svn-id: https://public-transport-enabler.googlecode.com/svn/trunk@181 0924bc21-9374-b0fa-ee44-9ff1593b38f0
This commit is contained in:
andreas.schildbach 2010-09-23 11:14:46 +00:00
parent e856c0a270
commit f8ebbc4054
3 changed files with 280 additions and 309 deletions

View file

@ -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(".*?" //
+ "(?:" //
+ "<table class=\"hafasResult\"[^>]*>(.+?)</table>.*?" //
+ "(?:<table cellspacing=\"0\" class=\"hafasResult\"[^>]*>(.+?)</table>|(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(".*?" //
+ "<td class=\"querysummary screennowrap\">\\s*(.*?)\\s*<a.*?" // location
+ "(\\d{2}\\.\\d{2}\\.\\d{2}).*?" // date
+ "Abfahrt (\\d+:\\d+).*?" // time
+ "%23(\\d+)&.*?" // locationId
, Pattern.DOTALL);
private static final Pattern P_DEPARTURES_COARSE = Pattern.compile("<tr class=\"depboard-(\\w*)\">\n(.*?)</tr>", Pattern.DOTALL);
static final Pattern P_DEPARTURES_FINE = Pattern.compile("" //
+ "<td class=\"[\\w ]*\">(\\d{1,2}:\\d{2})</td>\n.*?" // plannedTime
+ "(?:<td class=\"[\\w ]*?prognosis[\\w ]*?\">\n" //
+ "(?:&nbsp;|<span class=\"rtLimit\\d\">(p&#252;nktlich|\\d{1,2}:\\d{2})</span>)\n</td>\n" // predictedTime
+ ")?.*?" //
+ "<img src=\"/img/vs_oebb/(\\w+)_pic\\.gif\"\\s+alt=\"[^\"]*\">\\s*(.*?)\\s*</.*?" // type, line
+ "<span class=\"bold\">\n" //
+ "<a href=\"http://fahrplan\\.oebb\\.at/bin/stboard\\.exe/dn\\?ld=web\\d+&input=[^%]*?(?:%23(\\d+))?&.*?\">" // destinationId
+ "\\s*(.*?)\\s*</a>\n" // destination
+ "</span>.*?" //
+ "(?:<td class=\"center sepline top\">\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<Departure> departures = new ArrayList<Departure>(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<Departure> departures = new ArrayList<Departure>(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;
}