mirror of
https://gitlab.com/oeffi/public-transport-enabler.git
synced 2025-07-06 15:18:49 +00:00
Denmark departures
git-svn-id: https://public-transport-enabler.googlecode.com/svn/trunk@588 0924bc21-9374-b0fa-ee44-9ff1593b38f0
This commit is contained in:
parent
2cd4ebb926
commit
8833e42bb6
12 changed files with 719 additions and 513 deletions
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
|
@ -30,11 +31,15 @@ import java.util.TimeZone;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import de.schildbach.pte.dto.Connection;
|
||||
import de.schildbach.pte.dto.Departure;
|
||||
import de.schildbach.pte.dto.GetConnectionDetailsResult;
|
||||
import de.schildbach.pte.dto.Line;
|
||||
import de.schildbach.pte.dto.Location;
|
||||
|
@ -42,6 +47,8 @@ import de.schildbach.pte.dto.LocationType;
|
|||
import de.schildbach.pte.dto.NearbyStationsResult;
|
||||
import de.schildbach.pte.dto.QueryConnectionsResult;
|
||||
import de.schildbach.pte.dto.QueryConnectionsResult.Status;
|
||||
import de.schildbach.pte.dto.QueryDeparturesResult;
|
||||
import de.schildbach.pte.dto.StationDepartures;
|
||||
import de.schildbach.pte.util.Color;
|
||||
import de.schildbach.pte.util.ParserUtils;
|
||||
import de.schildbach.pte.util.XmlPullUtil;
|
||||
|
@ -193,6 +200,178 @@ public abstract class AbstractHafasProvider implements NetworkProvider
|
|||
}
|
||||
}
|
||||
|
||||
private static final Pattern P_AJAX_GET_STOPS_JSON = Pattern.compile("SLs\\.sls=(.*?);SLs\\.showSuggestion\\(\\);", Pattern.DOTALL);
|
||||
private static final Pattern P_AJAX_GET_STOPS_ID = Pattern.compile(".*?@L=(\\d+)@.*?");
|
||||
|
||||
protected final List<Location> ajaxGetStops(final String uri) throws IOException
|
||||
{
|
||||
final CharSequence page = ParserUtils.scrape(uri);
|
||||
|
||||
final Matcher mJson = P_AJAX_GET_STOPS_JSON.matcher(page);
|
||||
if (mJson.matches())
|
||||
{
|
||||
final String json = mJson.group(1);
|
||||
final List<Location> results = new ArrayList<Location>();
|
||||
|
||||
try
|
||||
{
|
||||
final JSONObject head = new JSONObject(json);
|
||||
final JSONArray aSuggestions = head.getJSONArray("suggestions");
|
||||
|
||||
for (int i = 0; i < aSuggestions.length(); i++)
|
||||
{
|
||||
final JSONObject suggestion = aSuggestions.optJSONObject(i);
|
||||
if (suggestion != null)
|
||||
{
|
||||
final int type = suggestion.getInt("type");
|
||||
final String value = suggestion.getString("value");
|
||||
final int lat = suggestion.getInt("ycoord");
|
||||
final int lon = suggestion.getInt("xcoord");
|
||||
int localId = 0;
|
||||
final Matcher m = P_AJAX_GET_STOPS_ID.matcher(suggestion.getString("id"));
|
||||
if (m.matches())
|
||||
localId = Integer.parseInt(m.group(1));
|
||||
|
||||
if (type == 1) // station
|
||||
{
|
||||
results.add(new Location(LocationType.STATION, localId, lat, lon, null, value));
|
||||
}
|
||||
else if (type == 2) // address
|
||||
{
|
||||
results.add(new Location(LocationType.ADDRESS, 0, lat, lon, null, value));
|
||||
}
|
||||
else if (type == 4) // poi
|
||||
{
|
||||
results.add(new Location(LocationType.POI, localId, lat, lon, null, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException("unknown type " + type + " on " + uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
catch (final JSONException x)
|
||||
{
|
||||
x.printStackTrace();
|
||||
throw new RuntimeException("cannot parse: '" + json + "' on " + uri, x);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("cannot parse: '" + page + "' on " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern P_XML_QUERY_DEPARTURES_COARSE = Pattern.compile("\\G<Journey (.*?)(?:/>|><HIMMessage (.*?)/></Journey>)(?:\n|\\z)",
|
||||
Pattern.DOTALL);
|
||||
private static final Pattern P_XML_QUERY_DEPARTURES_FINE = Pattern.compile("" //
|
||||
+ "fpTime\\s*=\"(\\d{1,2}:\\d{2})\"\\s*" // time
|
||||
+ "fpDate\\s*=\"(\\d{2}\\.\\d{2}\\.\\d{2})\"\\s*" // date
|
||||
+ "delay\\s*=\"(?:-|k\\.A\\.?|cancel|\\+?\\s*(\\d+))\"\\s*" // delay
|
||||
+ "(?:e_delay\\s*=\"\\d+\"\\s*)?" // (???)
|
||||
+ "(?:newpl\\s*=\"([^\"]*)\"\\s*)?" //
|
||||
+ "(?:platform\\s*=\"([^\"]*)\"\\s*)?" // position
|
||||
+ "targetLoc\\s*=\"([^\"]*)\"\\s*" // destination
|
||||
+ "(?:hafasname\\s*=\"[^\"]*\"\\s*)?" // (???)
|
||||
+ "prod\\s*=\"([^\"]*)\"\\s*" // line
|
||||
+ "(?:class\\s*=\"[^\"]*\"\\s*)?" // (???)
|
||||
+ "(?:dir\\s*=\"[^\"]*\"\\s*)?" // (destination)
|
||||
+ "(?:depStation\\s*=\"(.*?)\"\\s*)?" //
|
||||
+ "(?:delayReason\\s*=\"([^\"]*)\"\\s*)?" // message
|
||||
+ "(?:is_reachable\\s*=\"([^\"]*)\"\\s*)?" // (???)
|
||||
);
|
||||
private static final Pattern P_XML_QUERY_DEPARTURES_MESSAGES = Pattern.compile("<Err code=\"([^\"]*)\" text=\"([^\"]*)\"");
|
||||
|
||||
protected QueryDeparturesResult xmlQueryDepartures(final String uri, final int stationId) throws IOException
|
||||
{
|
||||
// scrape page
|
||||
final CharSequence page = ParserUtils.scrape(uri);
|
||||
|
||||
final QueryDeparturesResult result = new QueryDeparturesResult();
|
||||
|
||||
// parse page
|
||||
final Matcher mMessage = P_XML_QUERY_DEPARTURES_MESSAGES.matcher(page);
|
||||
if (mMessage.find())
|
||||
{
|
||||
final String code = mMessage.group(1);
|
||||
final String text = mMessage.group(2);
|
||||
|
||||
if (code.equals("H730")) // Your input is not valid
|
||||
return new QueryDeparturesResult(QueryDeparturesResult.Status.INVALID_STATION);
|
||||
if (code.equals("H890"))
|
||||
{
|
||||
result.stationDepartures.add(new StationDepartures(new Location(LocationType.STATION, stationId),
|
||||
Collections.<Departure> emptyList(), null));
|
||||
return result;
|
||||
}
|
||||
throw new IllegalArgumentException("unknown error " + code + ", " + text);
|
||||
}
|
||||
|
||||
final List<Departure> departures = new ArrayList<Departure>(8);
|
||||
|
||||
final Matcher mCoarse = P_XML_QUERY_DEPARTURES_COARSE.matcher(page);
|
||||
while (mCoarse.find())
|
||||
{
|
||||
// TODO parse HIMMessage
|
||||
|
||||
final Matcher mFine = P_XML_QUERY_DEPARTURES_FINE.matcher(mCoarse.group(1));
|
||||
if (mFine.matches())
|
||||
{
|
||||
if (mFine.group(8) == null)
|
||||
{
|
||||
final Calendar plannedTime = new GregorianCalendar(timeZone());
|
||||
plannedTime.clear();
|
||||
ParserUtils.parseEuropeanTime(plannedTime, mFine.group(1));
|
||||
ParserUtils.parseGermanDate(plannedTime, mFine.group(2));
|
||||
|
||||
final Calendar predictedTime;
|
||||
if (mFine.group(3) != null)
|
||||
{
|
||||
predictedTime = new GregorianCalendar(timeZone());
|
||||
predictedTime.setTimeInMillis(plannedTime.getTimeInMillis());
|
||||
predictedTime.add(Calendar.MINUTE, Integer.parseInt(mFine.group(3)));
|
||||
}
|
||||
else
|
||||
{
|
||||
predictedTime = null;
|
||||
}
|
||||
|
||||
// TODO parse newpl if present
|
||||
|
||||
final String position = mFine.group(5) != null ? "Gl. " + ParserUtils.resolveEntities(mFine.group(5)) : null;
|
||||
|
||||
final String destination = ParserUtils.resolveEntities(mFine.group(6)).trim();
|
||||
|
||||
final String line = normalizeLine(ParserUtils.resolveEntities(mFine.group(7)));
|
||||
|
||||
final String message;
|
||||
if (mFine.group(9) != null)
|
||||
{
|
||||
final String m = ParserUtils.resolveEntities(mFine.group(9)).trim();
|
||||
message = m.length() > 0 ? m : null;
|
||||
}
|
||||
else
|
||||
{
|
||||
message = null;
|
||||
}
|
||||
|
||||
departures.add(new Departure(plannedTime.getTime(), predictedTime != null ? predictedTime.getTime() : null, line,
|
||||
line != null ? lineColors(line) : null, null, position, 0, destination, message));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("cannot parse '" + mCoarse.group(1) + "' on " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
result.stationDepartures.add(new StationDepartures(new Location(LocationType.STATION, stationId), departures, null));
|
||||
return result;
|
||||
}
|
||||
|
||||
public QueryConnectionsResult queryConnections(Location from, Location via, Location to, final Date date, final boolean dep,
|
||||
final String products, final WalkSpeed walkSpeed) throws IOException
|
||||
{
|
||||
|
@ -620,6 +799,290 @@ public abstract class AbstractHafasProvider implements NetworkProvider
|
|||
throw new IllegalArgumentException(location.type.toString());
|
||||
}
|
||||
|
||||
private final static Pattern P_WHITESPACE = Pattern.compile("\\s+");
|
||||
|
||||
private final String normalizeWhitespace(final String str)
|
||||
{
|
||||
return P_WHITESPACE.matcher(str).replaceAll("");
|
||||
}
|
||||
|
||||
public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static final Pattern P_XML_NEARBY_STATIONS_COARSE = Pattern.compile("\\G<St\\s*(.*?)/?>(?:\n|\\z)", Pattern.DOTALL);
|
||||
private static final Pattern P_XML_NEARBY_STATIONS_FINE = Pattern.compile("" //
|
||||
+ "evaId=\"(\\d+)\"\\s*" // id
|
||||
+ "name=\"([^\"]+)\".*?" // name
|
||||
+ "x=\"(\\d+)\"\\s*" // x
|
||||
+ "y=\"(\\d+)\"\\s*" // y
|
||||
);
|
||||
private static final Pattern P_XML_NEARBY_STATIONS_MESSAGES = Pattern.compile("<Err code=\"([^\"]*)\" text=\"([^\"]*)\"");
|
||||
|
||||
protected final NearbyStationsResult xmlNearbyStations(final String uri) throws IOException
|
||||
{
|
||||
// scrape page
|
||||
final CharSequence page = ParserUtils.scrape(uri);
|
||||
|
||||
final List<Location> stations = new ArrayList<Location>();
|
||||
|
||||
// parse page
|
||||
final Matcher mMessage = P_XML_NEARBY_STATIONS_MESSAGES.matcher(page);
|
||||
if (mMessage.find())
|
||||
{
|
||||
final String code = mMessage.group(1);
|
||||
final String text = mMessage.group(2);
|
||||
|
||||
if (code.equals("H730")) // Your input is not valid
|
||||
return new NearbyStationsResult(NearbyStationsResult.Status.INVALID_STATION);
|
||||
if (code.equals("H890")) // No trains in result
|
||||
return new NearbyStationsResult(stations);
|
||||
throw new IllegalArgumentException("unknown error " + code + ", " + text);
|
||||
}
|
||||
|
||||
final Matcher mCoarse = P_XML_NEARBY_STATIONS_COARSE.matcher(page);
|
||||
while (mCoarse.find())
|
||||
{
|
||||
final Matcher mFine = P_XML_NEARBY_STATIONS_FINE.matcher(mCoarse.group(1));
|
||||
if (mFine.matches())
|
||||
{
|
||||
final int parsedId = Integer.parseInt(mFine.group(1));
|
||||
|
||||
final String parsedName = ParserUtils.resolveEntities(mFine.group(2)).trim();
|
||||
|
||||
final int parsedLon = Integer.parseInt(mFine.group(3));
|
||||
|
||||
final int parsedLat = Integer.parseInt(mFine.group(4));
|
||||
|
||||
stations.add(new Location(LocationType.STATION, parsedId, parsedLat, parsedLon, null, parsedName));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("cannot parse '" + mCoarse.group(1) + "' on " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
return new NearbyStationsResult(stations);
|
||||
}
|
||||
|
||||
private final static Pattern P_NEARBY_COARSE = Pattern.compile("<tr class=\"(zebra[^\"]*)\">(.*?)</tr>", Pattern.DOTALL);
|
||||
private final static Pattern P_NEARBY_FINE_COORDS = Pattern
|
||||
.compile("REQMapRoute0\\.Location0\\.X=(-?\\d+)&(?:amp;)?REQMapRoute0\\.Location0\\.Y=(-?\\d+)&");
|
||||
private final static Pattern P_NEARBY_FINE_LOCATION = Pattern.compile("[\\?&]input=(\\d+)&[^\"]*\">([^<]*)<");
|
||||
|
||||
protected abstract String nearbyStationUri(String stationId);
|
||||
|
||||
public NearbyStationsResult nearbyStations(final String stationId, final int lat, final int lon, final int maxDistance, final int maxStations)
|
||||
throws IOException
|
||||
{
|
||||
if (stationId == null)
|
||||
throw new IllegalArgumentException("stationId must be given");
|
||||
|
||||
final List<Location> stations = new ArrayList<Location>();
|
||||
|
||||
final String uri = nearbyStationUri(stationId);
|
||||
final CharSequence page = ParserUtils.scrape(uri);
|
||||
String oldZebra = null;
|
||||
|
||||
final Matcher mCoarse = P_NEARBY_COARSE.matcher(page);
|
||||
|
||||
while (mCoarse.find())
|
||||
{
|
||||
final String zebra = mCoarse.group(1);
|
||||
if (oldZebra != null && zebra.equals(oldZebra))
|
||||
throw new IllegalArgumentException("missed row? last:" + zebra);
|
||||
else
|
||||
oldZebra = zebra;
|
||||
|
||||
final Matcher mFineLocation = P_NEARBY_FINE_LOCATION.matcher(mCoarse.group(2));
|
||||
|
||||
if (mFineLocation.find())
|
||||
{
|
||||
int parsedLon = 0;
|
||||
int parsedLat = 0;
|
||||
final int parsedId = Integer.parseInt(mFineLocation.group(1));
|
||||
final String parsedName = ParserUtils.resolveEntities(mFineLocation.group(2));
|
||||
|
||||
final Matcher mFineCoords = P_NEARBY_FINE_COORDS.matcher(mCoarse.group(2));
|
||||
|
||||
if (mFineCoords.find())
|
||||
{
|
||||
parsedLon = Integer.parseInt(mFineCoords.group(1));
|
||||
parsedLat = Integer.parseInt(mFineCoords.group(2));
|
||||
}
|
||||
|
||||
final String[] nameAndPlace = splitNameAndPlace(parsedName);
|
||||
stations.add(new Location(LocationType.STATION, parsedId, parsedLat, parsedLon, nameAndPlace[0], nameAndPlace[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("cannot parse '" + mCoarse.group(2) + "' on " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxStations == 0 || maxStations >= stations.size())
|
||||
return new NearbyStationsResult(stations);
|
||||
else
|
||||
return new NearbyStationsResult(stations.subList(0, maxStations));
|
||||
}
|
||||
|
||||
protected static final Pattern P_NORMALIZE_LINE = Pattern.compile("([A-Za-zÄÖÜäöüßáàâéèêíìîóòôúùûØ/-]+)[\\s-]*(.*)");
|
||||
|
||||
protected String normalizeLine(final String type, final String line)
|
||||
{
|
||||
final char normalizedType = normalizeType(type);
|
||||
|
||||
if (normalizedType != 0)
|
||||
{
|
||||
if (line != null)
|
||||
{
|
||||
final Matcher m = P_NORMALIZE_LINE.matcher(line);
|
||||
final String strippedLine = m.matches() ? m.group(1) + m.group(2) : line;
|
||||
|
||||
return normalizedType + strippedLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Character.toString(normalizedType);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("cannot normalize type '" + type + "' line '" + line + "'");
|
||||
}
|
||||
|
||||
protected abstract char normalizeType(String type);
|
||||
|
||||
protected final char normalizeCommonTypes(final String ucType)
|
||||
{
|
||||
// Intercity
|
||||
if (ucType.equals("EC")) // EuroCity
|
||||
return 'I';
|
||||
if (ucType.equals("EN")) // EuroNight
|
||||
return 'I';
|
||||
if (ucType.equals("EIC")) // Ekspres InterCity, Polen
|
||||
return 'I';
|
||||
if (ucType.equals("ICE")) // InterCityExpress
|
||||
return 'I';
|
||||
if (ucType.equals("IC")) // InterCity
|
||||
return 'I';
|
||||
if (ucType.equals("ICT")) // InterCity
|
||||
return 'I';
|
||||
if (ucType.equals("CNL")) // CityNightLine
|
||||
return 'I';
|
||||
if (ucType.equals("OEC")) // ÖBB-EuroCity
|
||||
return 'I';
|
||||
if (ucType.equals("OIC")) // ÖBB-InterCity
|
||||
return 'I';
|
||||
if (ucType.equals("RJ")) // RailJet, Österreichische Bundesbahnen
|
||||
return 'I';
|
||||
if (ucType.equals("THA")) // Thalys
|
||||
return 'I';
|
||||
if (ucType.equals("TGV")) // Train à Grande Vitesse
|
||||
return 'I';
|
||||
if (ucType.equals("DNZ")) // Berlin-Saratov, Berlin-Moskva, Connections only?
|
||||
return 'I';
|
||||
if (ucType.equals("AIR")) // Generic Flight
|
||||
return 'I';
|
||||
if (ucType.equals("ECB")) // EC, Verona-München
|
||||
return 'I';
|
||||
if (ucType.equals("INZ")) // 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("IRX")) // IC
|
||||
return 'I';
|
||||
|
||||
// Regional
|
||||
if (ucType.equals("ZUG")) // Generic Train
|
||||
return 'R';
|
||||
if (ucType.equals("R")) // Generic Regional Train
|
||||
return 'R';
|
||||
if (ucType.equals("DPN")) // Dritter Personen Nahverkehr
|
||||
return 'R';
|
||||
if (ucType.equals("RB")) // RegionalBahn
|
||||
return 'R';
|
||||
if (ucType.equals("RE")) // RegionalExpress
|
||||
return 'R';
|
||||
if (ucType.equals("IR")) // Interregio
|
||||
return 'R';
|
||||
if (ucType.equals("IRE")) // Interregio Express
|
||||
return 'R';
|
||||
if (ucType.equals("HEX")) // Harz-Berlin-Express, Veolia
|
||||
return 'R';
|
||||
if (ucType.equals("WFB")) // Westfalenbahn
|
||||
return 'R';
|
||||
if (ucType.equals("RT")) // RegioTram
|
||||
return 'R';
|
||||
if (ucType.equals("REX")) // RegionalExpress, Österreich
|
||||
return 'R';
|
||||
if (ucType.equals("OS")) // Osobný vlak, Slovakia oder Osobní vlak, Czech Republic
|
||||
return 'R';
|
||||
if (ucType.equals("SP")) // Spěšný vlak, Czech Republic
|
||||
return 'R';
|
||||
|
||||
// Suburban Trains
|
||||
if (ucType.equals("S")) // Generic S-Bahn
|
||||
return 'S';
|
||||
|
||||
// Subway
|
||||
if (ucType.equals("U")) // Generic U-Bahn
|
||||
return 'U';
|
||||
|
||||
// Tram
|
||||
if (ucType.equals("STR")) // Generic Tram
|
||||
return 'T';
|
||||
|
||||
// Bus
|
||||
if (ucType.equals("BUS")) // Generic Bus
|
||||
return 'B';
|
||||
if (ucType.equals("AST")) // Anruf-Sammel-Taxi
|
||||
return 'B';
|
||||
if (ucType.equals("RUF")) // Rufbus
|
||||
return 'B';
|
||||
if (ucType.equals("SEV")) // Schienen-Ersatz-Verkehr
|
||||
return 'B';
|
||||
if (ucType.equals("BUSSEV")) // Schienen-Ersatz-Verkehr
|
||||
return 'B';
|
||||
if (ucType.equals("BSV")) // Bus SEV
|
||||
return 'B';
|
||||
if (ucType.equals("FB")) // Luxemburg-Saarbrücken
|
||||
return 'B';
|
||||
|
||||
// Ferry
|
||||
if (ucType.equals("SCH")) // Schiff
|
||||
return 'F';
|
||||
if (ucType.equals("AS")) // SyltShuttle, eigentlich Autoreisezug
|
||||
return 'F';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected String normalizeLine(final String line)
|
||||
{
|
||||
if (line == null || line.length() == 0)
|
||||
return null;
|
||||
|
||||
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 + " number " + number + " line " + line);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("cannot normalize line " + line);
|
||||
}
|
||||
|
||||
private static final Pattern P_LINE_S = Pattern.compile("S\\d+");
|
||||
private static final Pattern P_LINE_SN = Pattern.compile("SN\\d*");
|
||||
|
||||
|
@ -755,214 +1218,6 @@ public abstract class AbstractHafasProvider implements NetworkProvider
|
|||
+ longCategory + "'");
|
||||
}
|
||||
|
||||
private final static Pattern P_WHITESPACE = Pattern.compile("\\s+");
|
||||
|
||||
private final String normalizeWhitespace(final String str)
|
||||
{
|
||||
return P_WHITESPACE.matcher(str).replaceAll("");
|
||||
}
|
||||
|
||||
public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private final static Pattern P_NEARBY_COARSE = Pattern.compile("<tr class=\"(zebra[^\"]*)\">(.*?)</tr>", Pattern.DOTALL);
|
||||
private final static Pattern P_NEARBY_FINE_COORDS = Pattern
|
||||
.compile("REQMapRoute0\\.Location0\\.X=(-?\\d+)&(?:amp;)?REQMapRoute0\\.Location0\\.Y=(-?\\d+)&");
|
||||
private final static Pattern P_NEARBY_FINE_LOCATION = Pattern.compile("[\\?&]input=(\\d+)&[^\"]*\">([^<]*)<");
|
||||
|
||||
protected abstract String nearbyStationUri(String stationId);
|
||||
|
||||
public NearbyStationsResult nearbyStations(final String stationId, final int lat, final int lon, final int maxDistance, final int maxStations)
|
||||
throws IOException
|
||||
{
|
||||
if (stationId == null)
|
||||
throw new IllegalArgumentException("stationId must be given");
|
||||
|
||||
final List<Location> stations = new ArrayList<Location>();
|
||||
|
||||
final String uri = nearbyStationUri(stationId);
|
||||
final CharSequence page = ParserUtils.scrape(uri);
|
||||
String oldZebra = null;
|
||||
|
||||
final Matcher mCoarse = P_NEARBY_COARSE.matcher(page);
|
||||
|
||||
while (mCoarse.find())
|
||||
{
|
||||
final String zebra = mCoarse.group(1);
|
||||
if (oldZebra != null && zebra.equals(oldZebra))
|
||||
throw new IllegalArgumentException("missed row? last:" + zebra);
|
||||
else
|
||||
oldZebra = zebra;
|
||||
|
||||
final Matcher mFineLocation = P_NEARBY_FINE_LOCATION.matcher(mCoarse.group(2));
|
||||
|
||||
if (mFineLocation.find())
|
||||
{
|
||||
int parsedLon = 0;
|
||||
int parsedLat = 0;
|
||||
final int parsedId = Integer.parseInt(mFineLocation.group(1));
|
||||
final String parsedName = ParserUtils.resolveEntities(mFineLocation.group(2));
|
||||
|
||||
final Matcher mFineCoords = P_NEARBY_FINE_COORDS.matcher(mCoarse.group(2));
|
||||
|
||||
if (mFineCoords.find())
|
||||
{
|
||||
parsedLon = Integer.parseInt(mFineCoords.group(1));
|
||||
parsedLat = Integer.parseInt(mFineCoords.group(2));
|
||||
}
|
||||
|
||||
final String[] nameAndPlace = splitNameAndPlace(parsedName);
|
||||
stations.add(new Location(LocationType.STATION, parsedId, parsedLat, parsedLon, nameAndPlace[0], nameAndPlace[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("cannot parse '" + mCoarse.group(2) + "' on " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxStations == 0 || maxStations >= stations.size())
|
||||
return new NearbyStationsResult(stations);
|
||||
else
|
||||
return new NearbyStationsResult(stations.subList(0, maxStations));
|
||||
}
|
||||
|
||||
protected static final Pattern P_NORMALIZE_LINE = Pattern.compile("([A-Za-zÄÖÜäöüßáàâéèêíìîóòôúùû/-]+)[\\s-]*(.*)");
|
||||
|
||||
protected String normalizeLine(final String type, final String line)
|
||||
{
|
||||
final char normalizedType = normalizeType(type);
|
||||
|
||||
if (normalizedType != 0)
|
||||
{
|
||||
if (line != null)
|
||||
{
|
||||
final Matcher m = P_NORMALIZE_LINE.matcher(line);
|
||||
final String strippedLine = m.matches() ? m.group(1) + m.group(2) : line;
|
||||
|
||||
return normalizedType + strippedLine;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Character.toString(normalizedType);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("cannot normalize type '" + type + "' line '" + line + "'");
|
||||
}
|
||||
|
||||
protected abstract char normalizeType(String type);
|
||||
|
||||
protected final char normalizeCommonTypes(final String ucType)
|
||||
{
|
||||
// Intercity
|
||||
if (ucType.equals("EC")) // EuroCity
|
||||
return 'I';
|
||||
if (ucType.equals("EN")) // EuroNight
|
||||
return 'I';
|
||||
if (ucType.equals("EIC")) // Ekspres InterCity, Polen
|
||||
return 'I';
|
||||
if (ucType.equals("ICE")) // InterCityExpress
|
||||
return 'I';
|
||||
if (ucType.equals("IC")) // InterCity
|
||||
return 'I';
|
||||
if (ucType.equals("ICT")) // InterCity
|
||||
return 'I';
|
||||
if (ucType.equals("CNL")) // CityNightLine
|
||||
return 'I';
|
||||
if (ucType.equals("OEC")) // ÖBB-EuroCity
|
||||
return 'I';
|
||||
if (ucType.equals("OIC")) // ÖBB-InterCity
|
||||
return 'I';
|
||||
if (ucType.equals("RJ")) // RailJet, Österreichische Bundesbahnen
|
||||
return 'I';
|
||||
if (ucType.equals("THA")) // Thalys
|
||||
return 'I';
|
||||
if (ucType.equals("TGV")) // Train à Grande Vitesse
|
||||
return 'I';
|
||||
if (ucType.equals("DNZ")) // Berlin-Saratov, Berlin-Moskva, Connections only?
|
||||
return 'I';
|
||||
if (ucType.equals("AIR")) // Generic Flight
|
||||
return 'I';
|
||||
if (ucType.equals("ECB")) // EC, Verona-München
|
||||
return 'I';
|
||||
if (ucType.equals("INZ")) // 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("IRX")) // IC
|
||||
return 'I';
|
||||
|
||||
// Regional
|
||||
if (ucType.equals("ZUG")) // Generic Train
|
||||
return 'R';
|
||||
if (ucType.equals("R")) // Generic Regional Train
|
||||
return 'R';
|
||||
if (ucType.equals("DPN")) // Dritter Personen Nahverkehr
|
||||
return 'R';
|
||||
if (ucType.equals("RB")) // RegionalBahn
|
||||
return 'R';
|
||||
if (ucType.equals("RE")) // RegionalExpress
|
||||
return 'R';
|
||||
if (ucType.equals("IR")) // Interregio
|
||||
return 'R';
|
||||
if (ucType.equals("IRE")) // Interregio Express
|
||||
return 'R';
|
||||
if (ucType.equals("HEX")) // Harz-Berlin-Express, Veolia
|
||||
return 'R';
|
||||
if (ucType.equals("WFB")) // Westfalenbahn
|
||||
return 'R';
|
||||
if (ucType.equals("RT")) // RegioTram
|
||||
return 'R';
|
||||
if (ucType.equals("REX")) // RegionalExpress, Österreich
|
||||
return 'R';
|
||||
if (ucType.equals("OS")) // Osobný vlak, Slovakia oder Osobní vlak, Czech Republic
|
||||
return 'R';
|
||||
if (ucType.equals("SP")) // Spěšný vlak, Czech Republic
|
||||
return 'R';
|
||||
|
||||
// Suburban Trains
|
||||
if (ucType.equals("S")) // Generic S-Bahn
|
||||
return 'S';
|
||||
|
||||
// Subway
|
||||
if (ucType.equals("U")) // Generic U-Bahn
|
||||
return 'U';
|
||||
|
||||
// Tram
|
||||
if (ucType.equals("STR")) // Generic Tram
|
||||
return 'T';
|
||||
|
||||
// Bus
|
||||
if (ucType.equals("BUS")) // Generic Bus
|
||||
return 'B';
|
||||
if (ucType.equals("AST")) // Anruf-Sammel-Taxi
|
||||
return 'B';
|
||||
if (ucType.equals("RUF")) // Rufbus
|
||||
return 'B';
|
||||
if (ucType.equals("SEV")) // Schienen-Ersatz-Verkehr
|
||||
return 'B';
|
||||
if (ucType.equals("BUSSEV")) // Schienen-Ersatz-Verkehr
|
||||
return 'B';
|
||||
if (ucType.equals("BSV")) // Bus SEV
|
||||
return 'B';
|
||||
if (ucType.equals("FB")) // Luxemburg-Saarbrücken
|
||||
return 'B';
|
||||
|
||||
// Ferry
|
||||
if (ucType.equals("SCH")) // Schiff
|
||||
return 'F';
|
||||
if (ucType.equals("AS")) // SyltShuttle, eigentlich Autoreisezug
|
||||
return 'F';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static final Pattern P_CONNECTION_ID = Pattern.compile("co=(C\\d+-\\d+)&");
|
||||
|
||||
protected static String extractConnectionId(final String link)
|
||||
|
|
|
@ -20,7 +20,6 @@ package de.schildbach.pte;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
@ -28,7 +27,6 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import de.schildbach.pte.dto.Connection;
|
||||
import de.schildbach.pte.dto.Departure;
|
||||
import de.schildbach.pte.dto.GetConnectionDetailsResult;
|
||||
import de.schildbach.pte.dto.Line;
|
||||
import de.schildbach.pte.dto.Location;
|
||||
|
@ -36,7 +34,6 @@ import de.schildbach.pte.dto.LocationType;
|
|||
import de.schildbach.pte.dto.NearbyStationsResult;
|
||||
import de.schildbach.pte.dto.QueryConnectionsResult;
|
||||
import de.schildbach.pte.dto.QueryDeparturesResult;
|
||||
import de.schildbach.pte.dto.StationDepartures;
|
||||
import de.schildbach.pte.exception.SessionExpiredException;
|
||||
import de.schildbach.pte.util.ParserUtils;
|
||||
|
||||
|
@ -485,107 +482,18 @@ public final class BahnProvider extends AbstractHafasProvider
|
|||
}
|
||||
}
|
||||
|
||||
private String departuresQueryUri(final String stationId, final int maxDepartures)
|
||||
public QueryDeparturesResult queryDepartures(final String stationId, final int maxDepartures, final boolean equivs) throws IOException
|
||||
{
|
||||
final StringBuilder uri = new StringBuilder();
|
||||
uri.append(API_BASE).append("bhftafel.exe/dn");
|
||||
uri.append("?productsFilter=11111111111111");
|
||||
uri.append("&boardType=dep");
|
||||
// taken from railnavigator; ignore maxDepartures because result contains other stations
|
||||
uri.append("&maxJourneys=50");
|
||||
uri.append("&maxJourneys=50"); // ignore maxDepartures because result contains other stations
|
||||
uri.append("&start=yes");
|
||||
uri.append("&L=vs_java3");
|
||||
uri.append("&input=").append(stationId);
|
||||
return uri.toString();
|
||||
}
|
||||
|
||||
static final Pattern P_DEPARTURES_COARSE = Pattern.compile("\\G<Journey (.*?)/?>(?:\n|\\z)", Pattern.DOTALL);
|
||||
static final Pattern P_DEPARTURES_FINE = Pattern.compile("" //
|
||||
+ "fpTime=\"(\\d{1,2}:\\d{2})\" fpDate=\"(\\d{2}\\.\\d{2}\\.\\d{2})\" \n" // time, date
|
||||
+ "delay=\"(?:-|k\\.A\\.?|cancel|\\+?\\s*(\\d+))\" \n" // delay
|
||||
+ "(?:platform =\"([^\"]*)\" \n)?" // position
|
||||
+ "(?:newpl =\"([^\"]*)\" \n)?" //
|
||||
+ "targetLoc=\"(.*?)\" \n" // destination
|
||||
+ "prod=\"([^\"]*)\" \n" // line
|
||||
+ "(?:dir=[^\n]*\n)?" // (destination)
|
||||
+ "(?:depStation=\"(.*?)\"\n)?" //
|
||||
+ "delayReason=\"([^\"]*)\"\n" // message
|
||||
);
|
||||
private static final Pattern P_DEPARTURES_MESSAGES = Pattern.compile("<Err code=\"([^\"]*)\" text=\"([^\"]*)\"");
|
||||
|
||||
public QueryDeparturesResult queryDepartures(final String stationId, final int maxDepartures, final boolean equivs) throws IOException
|
||||
{
|
||||
final QueryDeparturesResult result = new QueryDeparturesResult();
|
||||
|
||||
// scrape page
|
||||
final String uri = departuresQueryUri(stationId, maxDepartures);
|
||||
final CharSequence page = ParserUtils.scrape(uri);
|
||||
|
||||
// parse page
|
||||
final Matcher mMessage = P_DEPARTURES_MESSAGES.matcher(page);
|
||||
if (mMessage.find())
|
||||
{
|
||||
final String code = mMessage.group(1);
|
||||
final String text = mMessage.group(2);
|
||||
|
||||
if (code.equals("H730")) // Your input is not valid
|
||||
return new QueryDeparturesResult(QueryDeparturesResult.Status.INVALID_STATION);
|
||||
if (code.equals("H890"))
|
||||
{
|
||||
result.stationDepartures.add(new StationDepartures(new Location(LocationType.STATION, Integer.parseInt(stationId)), Collections
|
||||
.<Departure> emptyList(), null));
|
||||
return result;
|
||||
}
|
||||
throw new IllegalArgumentException("unknown error " + code + ", " + text);
|
||||
}
|
||||
|
||||
final List<Departure> departures = new ArrayList<Departure>(8);
|
||||
|
||||
final Matcher mDepCoarse = P_DEPARTURES_COARSE.matcher(page);
|
||||
while (mDepCoarse.find())
|
||||
{
|
||||
final Matcher mDepFine = P_DEPARTURES_FINE.matcher(mDepCoarse.group(1));
|
||||
if (mDepFine.matches())
|
||||
{
|
||||
if (mDepFine.group(8) == null)
|
||||
{
|
||||
final Calendar plannedTime = new GregorianCalendar(timeZone());
|
||||
plannedTime.clear();
|
||||
ParserUtils.parseEuropeanTime(plannedTime, mDepFine.group(1));
|
||||
ParserUtils.parseGermanDate(plannedTime, mDepFine.group(2));
|
||||
|
||||
final Calendar predictedTime;
|
||||
if (mDepFine.group(3) != null)
|
||||
{
|
||||
predictedTime = new GregorianCalendar(timeZone());
|
||||
predictedTime.setTimeInMillis(plannedTime.getTimeInMillis());
|
||||
predictedTime.add(Calendar.MINUTE, Integer.parseInt(mDepFine.group(3)));
|
||||
}
|
||||
else
|
||||
{
|
||||
predictedTime = null;
|
||||
}
|
||||
|
||||
final String position = mDepFine.group(4) != null ? "Gl. " + ParserUtils.resolveEntities(mDepFine.group(4)) : null;
|
||||
|
||||
final String destination = ParserUtils.resolveEntities(mDepFine.group(6)).trim();
|
||||
|
||||
final String line = normalizeLine(ParserUtils.resolveEntities(mDepFine.group(7)));
|
||||
|
||||
final String message = ParserUtils.resolveEntities(mDepFine.group(9)).trim();
|
||||
|
||||
departures.add(new Departure(plannedTime.getTime(), predictedTime != null ? predictedTime.getTime() : null, line,
|
||||
line != null ? lineColors(line) : null, null, position, 0, destination, message.length() > 0 ? message : null));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("cannot parse '" + mDepCoarse.group(1) + "' on " + stationId);
|
||||
}
|
||||
}
|
||||
|
||||
result.stationDepartures.add(new StationDepartures(new Location(LocationType.STATION, Integer.parseInt(stationId)), departures, null));
|
||||
return result;
|
||||
return xmlQueryDepartures(uri.toString(), Integer.parseInt(stationId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -599,7 +507,8 @@ public final class BahnProvider extends AbstractHafasProvider
|
|||
private static final Pattern P_NORMALIZE_LINE_RUSSIA = Pattern.compile("(?:D\\s*)?(\\d{1,3}(?:[A-Z]{2}|Y))");
|
||||
private static final Pattern P_NORMALIZE_LINE_SBAHN = Pattern.compile("S\\w*\\d+");
|
||||
|
||||
private static String normalizeLine(final String line)
|
||||
@Override
|
||||
protected final String normalizeLine(final String line)
|
||||
{
|
||||
// TODO ARZ Simplon Tunnel: Brig - Iselle di Trasquera
|
||||
// ARZ29171
|
||||
|
|
|
@ -906,7 +906,8 @@ public final class BvgProvider extends AbstractHafasProvider
|
|||
private static final Pattern P_NORMALIZE_LINE_SPECIAL_NUMBER = Pattern.compile("\\d{4,}");
|
||||
private static final Pattern P_NORMALIZE_LINE_SPECIAL_BUS = Pattern.compile("Bus[A-Z]");
|
||||
|
||||
private static String normalizeLine(final String line)
|
||||
@Override
|
||||
protected String normalizeLine(final String line)
|
||||
{
|
||||
if (line == null || line.length() == 0)
|
||||
return null;
|
||||
|
|
173
src/de/schildbach/pte/DsbProvider.java
Normal file
173
src/de/schildbach/pte/DsbProvider.java
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright 2010, 2011 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.schildbach.pte;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import de.schildbach.pte.dto.Location;
|
||||
import de.schildbach.pte.dto.NearbyStationsResult;
|
||||
import de.schildbach.pte.dto.QueryDeparturesResult;
|
||||
import de.schildbach.pte.util.ParserUtils;
|
||||
|
||||
/**
|
||||
* @author Andreas Schildbach
|
||||
*/
|
||||
public class DsbProvider extends AbstractHafasProvider
|
||||
{
|
||||
public static final NetworkId NETWORK_ID = NetworkId.DSB;
|
||||
private static final String API_BASE = "http://mobil.rejseplanen.dk/mobil-bin/";
|
||||
|
||||
public DsbProvider()
|
||||
{
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
public NetworkId id()
|
||||
{
|
||||
return NETWORK_ID;
|
||||
}
|
||||
|
||||
public boolean hasCapabilities(final Capability... capabilities)
|
||||
{
|
||||
for (final Capability capability : capabilities)
|
||||
if (capability == Capability.DEPARTURES)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
private static final String AUTOCOMPLETE_URI = API_BASE + "ajax-getstop.exe/dn?getstop=1&REQ0JourneyStopsS0A=255&S=%s?&js=true&";
|
||||
private static final String ENCODING = "ISO-8859-1";
|
||||
|
||||
@Override
|
||||
public List<Location> autocompleteStations(final CharSequence constraint) throws IOException
|
||||
{
|
||||
final String uri = String.format(AUTOCOMPLETE_URI, ParserUtils.urlEncode(constraint.toString(), ENCODING));
|
||||
|
||||
return ajaxGetStops(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String nearbyStationUri(String stationId)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NearbyStationsResult nearbyStations(final String stationId, final int lat, final int lon, final int maxDistance, final int maxStations)
|
||||
throws IOException
|
||||
{
|
||||
final StringBuilder uri = new StringBuilder(API_BASE);
|
||||
uri.append("stboard.exe/dn");
|
||||
uri.append("?productsFilter=11111111111");
|
||||
uri.append("&boardType=dep");
|
||||
uri.append("&input=").append(ParserUtils.urlEncode(stationId));
|
||||
uri.append("&sTI=1&start=yes&hcount=0");
|
||||
uri.append("&L=vs_java3");
|
||||
|
||||
// &inputTripelId=A%3d1%40O%3dCopenhagen%20Airport%40X%3d12646941%40Y%3d55629753%40U%3d86%40L%3d900000011%40B%3d1
|
||||
|
||||
return xmlNearbyStations(uri.toString());
|
||||
}
|
||||
|
||||
private static final Pattern P_NORMALIZE_LINE_AND_TYPE = Pattern.compile("([^#]*)#(.*)");
|
||||
|
||||
@Override
|
||||
protected String normalizeLine(final String line)
|
||||
{
|
||||
final Matcher m = P_NORMALIZE_LINE_AND_TYPE.matcher(line);
|
||||
if (m.matches())
|
||||
{
|
||||
final String number = m.group(1).replaceAll("\\s+", " ");
|
||||
final String type = m.group(2);
|
||||
|
||||
final char normalizedType = normalizeType(type);
|
||||
if (normalizedType != 0)
|
||||
return normalizedType + number;
|
||||
|
||||
throw new IllegalStateException("cannot normalize type " + type + " number " + number + " line " + line);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("cannot normalize line " + line);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected char normalizeType(final String type)
|
||||
{
|
||||
final String ucType = type.toUpperCase();
|
||||
|
||||
if ("ICL".equals(ucType))
|
||||
return 'I';
|
||||
|
||||
if ("ØR".equals(ucType))
|
||||
return 'R';
|
||||
if ("RA".equals(ucType))
|
||||
return 'R';
|
||||
if ("RX".equals(ucType))
|
||||
return 'R';
|
||||
if ("PP".equals(ucType))
|
||||
return 'R';
|
||||
|
||||
if ("S-TOG".equals(ucType))
|
||||
return 'S';
|
||||
|
||||
if ("MET".equals(ucType))
|
||||
return 'U';
|
||||
|
||||
if ("BYBUS".equals(ucType))
|
||||
return 'B';
|
||||
if ("X-BUS".equals(ucType))
|
||||
return 'B';
|
||||
if ("HV-BUS".equals(ucType)) // Havnebus
|
||||
return 'B';
|
||||
if ("T-BUS".equals(ucType)) // Togbus
|
||||
return 'B';
|
||||
|
||||
if ("TELEBUS".equals(ucType))
|
||||
return 'P';
|
||||
if ("TELETAXI".equals(ucType))
|
||||
return 'P';
|
||||
|
||||
if ("FÆRGE".equals(ucType))
|
||||
return 'F';
|
||||
|
||||
final char t = normalizeCommonTypes(ucType);
|
||||
if (t != 0)
|
||||
return t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public QueryDeparturesResult queryDepartures(final String stationId, final int maxDepartures, final boolean equivs) throws IOException
|
||||
{
|
||||
final StringBuilder uri = new StringBuilder();
|
||||
uri.append(API_BASE).append("stboard.exe/dn");
|
||||
uri.append("?productsFilter=11111111111");
|
||||
uri.append("&boardType=dep");
|
||||
uri.append("&maxJourneys=50"); // ignore maxDepartures because result contains other stations
|
||||
uri.append("&start=yes");
|
||||
uri.append("&L=vs_java3");
|
||||
uri.append("&input=").append(stationId);
|
||||
|
||||
return xmlQueryDepartures(uri.toString(), Integer.parseInt(stationId));
|
||||
}
|
||||
}
|
|
@ -37,6 +37,9 @@ public enum NetworkId
|
|||
// Netherlands
|
||||
NS,
|
||||
|
||||
// Denmark
|
||||
DSB,
|
||||
|
||||
// United Kingdom
|
||||
TFL, TLEM, TLEA, TLSE, TLSW,
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ public class NsProvider extends AbstractHafasProvider
|
|||
{
|
||||
public static final NetworkId NETWORK_ID = NetworkId.NS;
|
||||
public static final String OLD_NETWORK_ID = "hafas.bene-system.com";
|
||||
private static final String API_URI = "http://hafas.bene-system.com/bin/extxml.exe";
|
||||
private static final String API_URI = "http://hafas.bene-system.com/bin/extxml.exe"; // http://plannerint.b-rail.be/bin/extxml.exe
|
||||
|
||||
private static final long PARSER_DAY_ROLLOVER_THRESHOLD_MS = 12 * 60 * 60 * 1000;
|
||||
|
||||
|
@ -179,27 +179,6 @@ public class NsProvider extends AbstractHafasProvider
|
|||
}
|
||||
}
|
||||
|
||||
private String normalizeLine(final String line)
|
||||
{
|
||||
if (line == null || line.length() == 0)
|
||||
return null;
|
||||
|
||||
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 + " number " + number + " line " + line);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("cannot normalize line " + line);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected char normalizeType(final String type)
|
||||
{
|
||||
|
|
|
@ -76,71 +76,13 @@ public class OebbProvider extends AbstractHafasProvider
|
|||
private static final String AUTOCOMPLETE_URI = API_BASE
|
||||
+ "ajax-getstop.exe/dny?start=1&tpl=suggest2json&REQ0JourneyStopsS0A=255&REQ0JourneyStopsB=12&S=%s?&js=true&";
|
||||
private static final String ENCODING = "ISO-8859-1";
|
||||
private static final Pattern P_AUTOCOMPLETE_JSON = Pattern.compile("SLs\\.sls=(.*?);SLs\\.showSuggestion\\(\\);", Pattern.DOTALL);
|
||||
private static final Pattern P_AUTOCOMPLETE_ID = Pattern.compile(".*?@L=(\\d+)@.*?");
|
||||
|
||||
@Override
|
||||
public List<Location> autocompleteStations(final CharSequence constraint) throws IOException
|
||||
{
|
||||
final String uri = String.format(AUTOCOMPLETE_URI, ParserUtils.urlEncode(constraint.toString(), ENCODING));
|
||||
final CharSequence page = ParserUtils.scrape(uri);
|
||||
|
||||
final Matcher mJson = P_AUTOCOMPLETE_JSON.matcher(page);
|
||||
if (mJson.matches())
|
||||
{
|
||||
final String json = mJson.group(1);
|
||||
final List<Location> results = new ArrayList<Location>();
|
||||
|
||||
try
|
||||
{
|
||||
final JSONObject head = new JSONObject(json);
|
||||
final JSONArray aSuggestions = head.getJSONArray("suggestions");
|
||||
|
||||
for (int i = 0; i < aSuggestions.length(); i++)
|
||||
{
|
||||
final JSONObject suggestion = aSuggestions.optJSONObject(i);
|
||||
if (suggestion != null)
|
||||
{
|
||||
final int type = suggestion.getInt("type");
|
||||
final String value = suggestion.getString("value");
|
||||
final int lat = suggestion.getInt("ycoord");
|
||||
final int lon = suggestion.getInt("xcoord");
|
||||
int localId = 0;
|
||||
final Matcher m = P_AUTOCOMPLETE_ID.matcher(suggestion.getString("id"));
|
||||
if (m.matches())
|
||||
localId = Integer.parseInt(m.group(1));
|
||||
|
||||
if (type == 1) // station
|
||||
{
|
||||
results.add(new Location(LocationType.STATION, localId, lat, lon, null, value));
|
||||
}
|
||||
else if (type == 2) // address
|
||||
{
|
||||
results.add(new Location(LocationType.ADDRESS, 0, lat, lon, null, value));
|
||||
}
|
||||
else if (type == 4) // poi
|
||||
{
|
||||
results.add(new Location(LocationType.POI, localId, lat, lon, null, value));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException("unknown type " + type + " on " + uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
catch (final JSONException x)
|
||||
{
|
||||
x.printStackTrace();
|
||||
throw new RuntimeException("cannot parse: '" + json + "' on " + uri, x);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("cannot parse: '" + page + "' on " + uri);
|
||||
}
|
||||
return ajaxGetStops(uri);
|
||||
}
|
||||
|
||||
private final String NEARBY_URI = API_BASE + "stboard.exe/dn?distance=50&near=Suchen&input=%s";
|
||||
|
@ -604,7 +546,8 @@ public class OebbProvider extends AbstractHafasProvider
|
|||
private static final Pattern P_NORMALIZE_LINE_RUSSIA = Pattern.compile("\\d{1,3}[A-Z]{2}");
|
||||
private static final Pattern P_NORMALIZE_LINE_RUSSIA_INT = Pattern.compile("\\d{3}Y");
|
||||
|
||||
private String normalizeLine(final String line)
|
||||
@Override
|
||||
protected String normalizeLine(final String line)
|
||||
{
|
||||
final Matcher m = P_NORMALIZE_LINE.matcher(line);
|
||||
if (m.matches())
|
||||
|
|
|
@ -603,7 +603,8 @@ public class RmvProvider extends AbstractHafasProvider
|
|||
}
|
||||
}
|
||||
|
||||
private static String normalizeLine(final String line)
|
||||
@Override
|
||||
protected String normalizeLine(final String line)
|
||||
{
|
||||
if (line == null || line.length() == 0)
|
||||
return null;
|
||||
|
|
|
@ -202,27 +202,6 @@ public class SbbProvider extends AbstractHafasProvider
|
|||
}
|
||||
}
|
||||
|
||||
private String normalizeLine(final String line)
|
||||
{
|
||||
if (line == null || line.length() == 0)
|
||||
return null;
|
||||
|
||||
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 + " number " + number + " line " + line);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("cannot normalize line " + line);
|
||||
}
|
||||
|
||||
private static final Pattern P_NORMALIZE_TYPE_SBAHN = Pattern.compile("SN?\\d*");
|
||||
private static final Pattern P_NORMALIZE_TYPE_BUS = Pattern.compile("BUS\\w*");
|
||||
|
||||
|
|
|
@ -179,27 +179,6 @@ public class SncbProvider extends AbstractHafasProvider
|
|||
}
|
||||
}
|
||||
|
||||
private String normalizeLine(final String line)
|
||||
{
|
||||
if (line == null || line.length() == 0)
|
||||
return null;
|
||||
|
||||
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 + " number " + number + " line " + line);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("cannot normalize line " + line);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected char normalizeType(final String type)
|
||||
{
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package de.schildbach.pte;
|
||||
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
@ -29,49 +28,6 @@ import org.junit.Test;
|
|||
*/
|
||||
public class BahnProviderTest
|
||||
{
|
||||
@Test
|
||||
public void coarseDeparture()
|
||||
{
|
||||
assertCoarseDepartures("" //
|
||||
+ "<Journey fpTime=\"17:45\" fpDate=\"02.10.10\" \n" //
|
||||
+ "delay=\"k.A\" \n" //
|
||||
+ "platform =\"1\" \n" //
|
||||
+ "targetLoc=\"Berlin Südkreuz (S)\" \n" //
|
||||
+ "prod=\"S 41\" \n" //
|
||||
+ "depStation=\"Berlin Sonnenallee\"\n" //
|
||||
+ "delayReason=\" \"\n" //
|
||||
+ "/>\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coarseDepartureWithoutTrailingNewLine()
|
||||
{
|
||||
assertCoarseDepartures("" //
|
||||
+ "<Journey fpTime=\"17:45\" fpDate=\"02.10.10\" \n" //
|
||||
+ "delay=\"k.A\" \n" //
|
||||
+ "platform =\"1\" \n" //
|
||||
+ "targetLoc=\"Berlin Südkreuz (S)\" \n" //
|
||||
+ "prod=\"S 41\" \n" //
|
||||
+ "depStation=\"Berlin Sonnenallee\"\n" //
|
||||
+ "delayReason=\" \"\n" //
|
||||
+ "/>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coarseDepartureWithInvalidClosingBracket()
|
||||
{
|
||||
assertCoarseDepartures("" //
|
||||
+ "<Journey fpTime=\"17:45\" fpDate=\"02.10.10\" \n" //
|
||||
+ "delay=\"k.A\" \n" //
|
||||
+ "platform =\"1\" \n" //
|
||||
+ "targetLoc=\"Berlin Südkreuz (S)\" \n" //
|
||||
+ "prod=\"S 41\" \n" //
|
||||
+ "dir=\"Ringbahn ->\"\n" //
|
||||
+ "depStation=\"Berlin Sonnenallee\"\n" //
|
||||
+ "delayReason=\" \"\n" //
|
||||
+ "/>\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectionUebergang()
|
||||
{
|
||||
|
@ -82,21 +38,6 @@ public class BahnProviderTest
|
|||
+ "<span class=\"bold\">Berlin-Lichtenberg</span><br />\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newDepartureWithMessage()
|
||||
{
|
||||
final Matcher m = assertFineDepartures("" //
|
||||
+ "fpTime=\"21:10\" fpDate=\"01.10.10\" \n" //
|
||||
+ "delay=\"cancel\" \n" //
|
||||
+ "platform =\"1\" \n" //
|
||||
+ "targetLoc=\"Magdeburg Hbf\" \n" //
|
||||
+ "prod=\"RE 38090\" \n" //
|
||||
+ "delayReason=\" Notarzteinsatz am Gleis\"\n");
|
||||
|
||||
assertNotNull(m.group(4)); // position
|
||||
assertNotNull(m.group(9)); // message
|
||||
}
|
||||
|
||||
private void assertFineConnectionDetails(final String s)
|
||||
{
|
||||
Matcher m = BahnProvider.P_CONNECTION_DETAILS_FINE.matcher(s);
|
||||
|
@ -104,28 +45,4 @@ public class BahnProviderTest
|
|||
|
||||
// ParserUtils.printGroups(m);
|
||||
}
|
||||
|
||||
private Matcher assertCoarseDepartures(final String s)
|
||||
{
|
||||
Matcher m = BahnProvider.P_DEPARTURES_COARSE.matcher(s);
|
||||
assertTrue(m.find());
|
||||
assertFineDepartures(m.group(1));
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
private Matcher assertFineDepartures(final String s)
|
||||
{
|
||||
Matcher m = BahnProvider.P_DEPARTURES_FINE.matcher(s);
|
||||
assertTrue(m.matches());
|
||||
|
||||
// ParserUtils.printGroups(m);
|
||||
|
||||
assertNotNull(m.group(1)); // time
|
||||
assertNotNull(m.group(2)); // date
|
||||
assertNotNull(m.group(6)); // destination
|
||||
assertNotNull(m.group(7)); // line
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
|
67
test/de/schildbach/pte/live/DsbProviderLiveTest.java
Normal file
67
test/de/schildbach/pte/live/DsbProviderLiveTest.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2010, 2011 the original author or authors.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.schildbach.pte.live;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import de.schildbach.pte.DsbProvider;
|
||||
import de.schildbach.pte.dto.Location;
|
||||
import de.schildbach.pte.dto.NearbyStationsResult;
|
||||
import de.schildbach.pte.dto.QueryDeparturesResult;
|
||||
|
||||
/**
|
||||
* @author Andreas Schildbach
|
||||
*/
|
||||
public class DsbProviderLiveTest
|
||||
{
|
||||
private final DsbProvider provider = new DsbProvider();
|
||||
|
||||
@Test
|
||||
public void autocomplete() throws Exception
|
||||
{
|
||||
final List<Location> autocompletes = provider.autocompleteStations("Airport");
|
||||
|
||||
list(autocompletes);
|
||||
}
|
||||
|
||||
private void list(final List<Location> autocompletes)
|
||||
{
|
||||
System.out.print(autocompletes.size() + " ");
|
||||
for (final Location autocomplete : autocompletes)
|
||||
System.out.print(autocomplete.toDebugString() + " ");
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nearbyStation() throws Exception
|
||||
{
|
||||
final NearbyStationsResult result = provider.nearbyStations("8600858", 0, 0, 0, 0);
|
||||
|
||||
System.out.println(result.stations.size() + " " + result.stations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryDepartures() throws Exception
|
||||
{
|
||||
final QueryDeparturesResult result = provider.queryDepartures("8600858", 0, false);
|
||||
|
||||
System.out.println(result.stationDepartures);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue