diff --git a/src/de/schildbach/pte/AbstractEfaProvider.java b/src/de/schildbach/pte/AbstractEfaProvider.java index ce38bb59..7af32e8c 100644 --- a/src/de/schildbach/pte/AbstractEfaProvider.java +++ b/src/de/schildbach/pte/AbstractEfaProvider.java @@ -74,10 +74,7 @@ public abstract class AbstractEfaProvider implements NetworkProvider return results; } - private static final Pattern P_NEARBY = Pattern.compile("]*? nameWithPlace=\"([^\"]*)\" distance=\"(\\d+)\"" // - + "|distance=\"(\\d+)\" [^>]*? nameWithPlace=\"([^\"]*)\" [^>]*? stopID=\"(\\d+)\" [^>]*? x=\"(\\d+)\" y=\"(\\d+)\"" // - + ")"); + private static final Pattern P_NEARBY_MESSAGES = Pattern.compile("(unsere Server zur Zeit ausgelastet)"); protected abstract String nearbyLatLonUri(int lat, int lon); @@ -87,39 +84,95 @@ public abstract class AbstractEfaProvider implements NetworkProvider throws IOException { String uri = null; - if (lat != 0 || lon != 0) - uri = nearbyLatLonUri(lat, lon); if (uri == null && stationId != null) uri = nearbyStationUri(stationId); + if (uri == null && (lat != 0 || lon != 0)) + uri = nearbyLatLonUri(lat, lon); if (uri == null) throw new IllegalArgumentException("at least one of stationId or lat/lon must be given"); - final CharSequence page = ParserUtils.scrape(uri); - - final List stations = new ArrayList(); - - final Matcher mNearby = P_NEARBY.matcher(page); - while (mNearby.find()) + try { - final boolean firstSyntax = mNearby.group(1) != null; - final int sId = Integer.parseInt(mNearby.group(firstSyntax ? 1 : 8)); - final int sLon = Integer.parseInt(mNearby.group(firstSyntax ? 2 : 9)); - final int sLat = Integer.parseInt(mNearby.group(firstSyntax ? 3 : 10)); - final String sName = mNearby.group(firstSyntax ? 4 : 7).trim(); - final int sDist = Integer.parseInt(mNearby.group(firstSyntax ? 5 : 6)); + final CharSequence page = ParserUtils.scrape(uri); - final Station station = new Station(sId, sName, sLat, sLon, sDist, null, null); - stations.add(station); + if (P_NEARBY_MESSAGES.matcher(page).find()) + return null; + + final XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); + final XmlPullParser pp = factory.newPullParser(); + pp.setInput(new StringReader(page.toString())); + + XmlPullUtil.jumpToStartTag(pp, null, "itdOdvName"); + final String nameState = pp.getAttributeValue(null, "state"); + if (nameState.equals("identified")) + { + final List stations = new ArrayList(); + + XmlPullUtil.jumpToStartTag(pp, null, "odvNameElem"); + final String parsedOwnLocationIdStr = pp.getAttributeValue(null, "stopID"); + if (parsedOwnLocationIdStr != null) + { + final int parsedOwnLocationId = Integer.parseInt(parsedOwnLocationIdStr); + final String parsedOwnMapName = pp.getAttributeValue(null, "mapName"); + if (parsedOwnMapName != null) + { + if (!"WGS84".equals(parsedOwnMapName)) + throw new IllegalStateException("unknown mapName: " + parsedOwnMapName); + final int parsedOwnLon = Integer.parseInt(pp.getAttributeValue(null, "x")); + final int parsedOwnLat = Integer.parseInt(pp.getAttributeValue(null, "y")); + stations.add(new Station(parsedOwnLocationId, null, parsedOwnLat, parsedOwnLon, 0, null, null)); + } + } + + if (XmlPullUtil.jumpToStartTag(pp, null, "itdOdvAssignedStops")) + { + while (XmlPullUtil.nextStartTagInsideTree(pp, null, "itdOdvAssignedStop")) + { + final String parsedMapName = pp.getAttributeValue(null, "mapName"); + if (parsedMapName != null) + { + if (!"WGS84".equals(parsedMapName)) + throw new IllegalStateException("unknown mapName: " + parsedMapName); + + final int parsedLocationId = Integer.parseInt(pp.getAttributeValue(null, "stopID")); + final String parsedName = normalizeLocationName(pp.getAttributeValue(null, "nameWithPlace")); + final int parsedLon = Integer.parseInt(pp.getAttributeValue(null, "x")); + final int parsedLat = Integer.parseInt(pp.getAttributeValue(null, "y")); + final String parsedDistStr = pp.getAttributeValue(null, "distance"); + final int parsedDist = parsedDistStr != null ? Integer.parseInt(parsedDistStr) : 0; + + stations.add(new Station(parsedLocationId, parsedName, parsedLat, parsedLon, parsedDist, null, null)); + + XmlPullUtil.skipRestOfTree(pp); + } + } + } + + if (maxStations == 0 || maxStations >= stations.size()) + return stations; + else + return stations.subList(0, maxStations); + } + else if (nameState.equals("notidentified")) + { + return null; + } + else + { + throw new RuntimeException("unknown nameState '" + nameState + "' on " + uri); + } + } + catch (final XmlPullParserException x) + { + throw new RuntimeException(x); } - - if (maxStations == 0 || maxStations >= stations.size()) - return stations; - else - return stations.subList(0, maxStations); } + private static final Pattern P_LINE_IRE = Pattern.compile("IRE\\d+"); private static final Pattern P_LINE_RE = Pattern.compile("RE\\d+"); private static final Pattern P_LINE_RB = Pattern.compile("RB\\d+"); + private static final Pattern P_LINE_VB = Pattern.compile("VB\\d+"); + private static final Pattern P_LINE_R = Pattern.compile("R\\d+(/R\\d+|\\(z\\))?"); private static final Pattern P_LINE_U = Pattern.compile("U\\d+"); protected String parseLine(final String number, final String symbol, final String mot) @@ -157,6 +210,8 @@ public abstract class AbstractEfaProvider implements NetworkProvider return 'R' + str; if (type.equals("IRE")) // Interregio-Express return 'R' + str; + if (P_LINE_IRE.matcher(type).matches()) + return 'R' + str; if (type.equals("RE")) // Regional-Express return 'R' + str; if (P_LINE_RE.matcher(type).matches()) @@ -167,6 +222,8 @@ public abstract class AbstractEfaProvider implements NetworkProvider return 'R' + str; if (type.equals("R")) // Regionalzug return 'R' + str; + if (P_LINE_R.matcher(type).matches()) + return 'R' + str; if (type.equals("D")) // Schnellzug return 'R' + str; if (type.equals("WFB")) // Westfalenbahn @@ -209,6 +266,10 @@ public abstract class AbstractEfaProvider implements NetworkProvider return 'R' + str; if (type.equals("VBG")) // Vogtlandbahn return 'R' + str; + if (type.equals("VB")) // Vogtlandbahn + return 'R' + str; + if (P_LINE_VB.matcher(type).matches()) + return 'R' + str; if (type.equals("VX")) // Vogtland Express return 'R' + str; if (type.equals("CB")) // City-Bahn Chemnitz @@ -279,6 +340,24 @@ public abstract class AbstractEfaProvider implements NetworkProvider return 'R' + str; if (type.equals("PRE")) // Pressnitztalbahn return 'R' + str; + if (type.equals("VEB")) // Vulkan-Eifel-Bahn + return 'R' + str; + if (type.equals("neg")) // Norddeutsche Eisenbahn Gesellschaft + return 'R' + str; + if (type.equals("AVG")) // Felsenland-Express + return 'R' + str; + if (type.equals("ABG")) // Anhaltische Bahngesellschaft + return 'R' + str; + if (type.equals("LGB")) // Lößnitzgrundbahn + return 'R' + str; + if (type.equals("LEO")) // Chiemgauer Lokalbahn + return 'R' + str; + if (type.equals("WTB")) // Weißeritztalbahn + return 'R' + str; + if (type.equals("P")) // Kasbachtalbahn, Wanderbahn im Regental, Rhön-Zügle + return 'R' + str; + if (type.equals("KBS")) // Kursbuchstrecke + return 'R' + str; if (type.equals("BSB")) // Breisgau-S-Bahn return 'S' + str; @@ -414,7 +493,7 @@ public abstract class AbstractEfaProvider implements NetworkProvider private static final Pattern P_STATION_NAME_WHITESPACE = Pattern.compile("\\s+"); - private static String normalizeLocationName(final String name) + protected static String normalizeLocationName(final String name) { return P_STATION_NAME_WHITESPACE.matcher(name).replaceAll(" "); } diff --git a/src/de/schildbach/pte/VrrProvider.java b/src/de/schildbach/pte/VrrProvider.java new file mode 100644 index 00000000..7918c735 --- /dev/null +++ b/src/de/schildbach/pte/VrrProvider.java @@ -0,0 +1,114 @@ +/* + * Copyright 2010 the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.schildbach.pte; + +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * @author Andreas Schildbach + */ +public class VrrProvider extends AbstractEfaProvider +{ + public static final String NETWORK_ID = "efa3.vrr.de"; + private static final String API_BASE = "http://efa3.vrr.de/vrr_mobile/"; + + public boolean hasCapabilities(final Capability... capabilities) + { + for (final Capability capability : capabilities) + if (capability == Capability.DEPARTURES) + return true; + + return false; + } + + @Override + protected String autocompleteUri(CharSequence constraint) + { + throw new UnsupportedOperationException(); + } + + @Override + protected String nearbyLatLonUri(final int lat, final int lon) + { + return null; + } + + private static final String NEARBY_STATION_URI = API_BASE + + "XSLT_DM_REQUEST" + + "?outputFormat=XML&coordOutputFormat=WGS84&name_dm=%s&type_dm=stop&itOptionsActive=1&ptOptionsActive=1&useProxFootSearch=1&mergeDep=1&useAllStops=1&mode=direct&deleteAssignedStop=0"; + + @Override + protected String nearbyStationUri(final String stationId) + { + return String.format(NEARBY_STATION_URI, stationId); + } + + public StationLocationResult stationLocation(String stationId) throws IOException + { + throw new UnsupportedOperationException(); + } + + public String departuresQueryUri(final String stationId, final int maxDepartures) + { + final StringBuilder uri = new StringBuilder(); + uri.append(API_BASE).append("XSLT_DM_REQUEST"); + uri.append("?outputFormat=XML"); + uri.append("&coordOutputFormat=WGS84"); + uri.append("&type_dm=stop"); + uri.append("&name_dm=").append(stationId); + uri.append("&mode=direct"); + return uri.toString(); + } + + public QueryConnectionsResult queryConnections(LocationType fromType, String from, LocationType viaType, String via, LocationType toType, + String to, Date date, boolean dep, WalkSpeed walkSpeed) throws IOException + { + throw new UnsupportedOperationException(); + } + + public QueryConnectionsResult queryMoreConnections(String uri) throws IOException + { + throw new UnsupportedOperationException(); + } + + public GetConnectionDetailsResult getConnectionDetails(String connectionUri) throws IOException + { + throw new UnsupportedOperationException(); + } + + private static final Map LINES = new HashMap(); + + static + { + LINES.put('I', new int[] { Color.WHITE, Color.RED, Color.RED }); + LINES.put('R', new int[] { Color.GRAY, Color.WHITE }); + LINES.put('S', new int[] { Color.parseColor("#006e34"), Color.WHITE }); + LINES.put('U', new int[] { Color.parseColor("#003090"), Color.WHITE }); + LINES.put('T', new int[] { Color.parseColor("#cc0000"), Color.WHITE }); + LINES.put('B', new int[] { Color.parseColor("#993399"), Color.WHITE }); + LINES.put('F', new int[] { Color.BLUE, Color.WHITE }); + LINES.put('?', new int[] { Color.DKGRAY, Color.WHITE }); + } + + public int[] lineColors(final String line) + { + return LINES.get(line.charAt(0)); + } +} diff --git a/test/de/schildbach/pte/live/VrrProviderLiveTest.java b/test/de/schildbach/pte/live/VrrProviderLiveTest.java new file mode 100644 index 00000000..c96833e1 --- /dev/null +++ b/test/de/schildbach/pte/live/VrrProviderLiveTest.java @@ -0,0 +1,49 @@ +/* + * Copyright 2010 the original author or authors. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.schildbach.pte.live; + +import java.util.List; + +import org.junit.Test; + +import de.schildbach.pte.QueryDeparturesResult; +import de.schildbach.pte.Station; +import de.schildbach.pte.VrrProvider; + +/** + * @author Andreas Schildbach + */ +public class VrrProviderLiveTest +{ + private final VrrProvider provider = new VrrProvider(); + + @Test + public void nearbyStation() throws Exception + { + final List results = provider.nearbyStations("20019904", 0, 0, 0, 0); + + System.out.println(results.size() + " " + results); + } + + @Test + public void departures() throws Exception + { + final QueryDeparturesResult result = provider.queryDepartures(provider.departuresQueryUri("1007258", 0)); + + System.out.println(result.departures.size() + " " + result.departures); + } +}