Migrate Liechtenstein, Bregenz & Vorarlberg to use VAO.

This commit is contained in:
Andreas Schildbach 2015-09-10 14:58:02 +02:00
parent 9b16fc6353
commit 4c851ae4c5
7 changed files with 901 additions and 155 deletions

View file

@ -32,11 +32,13 @@ import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Currency;
import java.util.Date;
import java.util.EnumSet;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@ -58,6 +60,7 @@ import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import de.schildbach.pte.dto.Departure;
import de.schildbach.pte.dto.Fare;
import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Line.Attr;
import de.schildbach.pte.dto.Location;
@ -91,14 +94,19 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider
protected final static String SERVER_PRODUCT = "hafas";
private static final String REQC_PROD = "hafas";
protected static final int DEFAULT_MAX_DEPARTURES = 100;
protected static final int DEFAULT_MAX_LOCATIONS = 50;
protected String stationBoardEndpoint;
protected String getStopEndpoint;
protected String queryEndpoint;
protected final String mgateEndpoint;
private @Nullable String extXmlEndpoint = null;
private Product[] productsMap;
private @Nullable String accessId = null;
private @Nullable String clientType = "ANDROID";
private @Nullable String jsonApiVersion;
private @Nullable String jsonApiAuthorization;
private @Nullable String jsonApiClient;
private Charset jsonGetStopsEncoding = Charsets.ISO_8859_1;
private boolean jsonGetStopsUseWeight = true;
private Charset jsonNearbyLocationsEncoding = Charsets.ISO_8859_1;
@ -133,6 +141,38 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider
}
}
@SuppressWarnings("serial")
public static class JsonContext implements QueryTripsContext
{
public final Location from, to;
public final Date date;
public final boolean dep;
public final Set<Product> products;
public final String laterContext, earlierContext;
public JsonContext(final Location from, final Location to, final Date date, final boolean dep, final Set<Product> products,
final String laterContext, final String earlierContext)
{
this.from = from;
this.to = to;
this.date = date;
this.dep = dep;
this.products = products;
this.laterContext = laterContext;
this.earlierContext = earlierContext;
}
public boolean canQueryLater()
{
return laterContext != null;
}
public boolean canQueryEarlier()
{
return earlierContext != null;
}
}
@SuppressWarnings("serial")
public static class QueryTripsBinaryContext implements QueryTripsContext
{
@ -169,6 +209,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider
this.stationBoardEndpoint = apiBase + "stboard.exe/" + apiLanguage;
this.getStopEndpoint = apiBase + "ajax-getstop.exe/" + apiLanguage;
this.queryEndpoint = apiBase + "query.exe/" + apiLanguage;
this.mgateEndpoint = apiBase + "mgate.exe";
this.productsMap = productsMap;
}
@ -192,14 +233,29 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider
this.extXmlEndpoint = extXmlEndpoint;
}
protected void setAccessId(final String accessId)
{
this.accessId = accessId;
}
protected void setClientType(final String clientType)
{
this.clientType = clientType;
}
protected void setAccessId(final String accessId)
protected void setJsonApiVersion(final String jsonApiVersion)
{
this.accessId = accessId;
this.jsonApiVersion = jsonApiVersion;
}
protected void setJsonApiAuthorization(final String jsonApiAuthorization)
{
this.jsonApiAuthorization = jsonApiAuthorization;
}
protected void setJsonApiClient(final String jsonApiClient)
{
this.jsonApiClient = jsonApiClient;
}
protected void setDominantPlanStopTime(final boolean dominantPlanStopTime)
@ -406,7 +462,7 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider
public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException
{
final StringBuilder uri = new StringBuilder(getStopEndpoint);
appendJsonGetStopsParameters(uri, constraint, 0);
appendJsonGetStopsParameters(uri, checkNotNull(constraint), 0);
return jsonGetStops(uri.toString());
}
@ -826,6 +882,557 @@ public abstract class AbstractHafasProvider extends AbstractNetworkProvider
{
}
protected final NearbyLocationsResult jsonLocGeoPos(final EnumSet<LocationType> types, final int lat, final int lon) throws IOException
{
final boolean getPOIs = types.contains(LocationType.POI);
final String request = wrapJsonApiRequest("LocGeoPos", "{\"ring\":" //
+ "{\"cCrd\":{\"x\":" + lon + ",\"y\":" + lat + "}}," //
+ "\"getPOIs\":" + getPOIs + "}", //
false);
final String uri = checkNotNull(mgateEndpoint);
final CharSequence page = httpClient.get(uri, request, "application/json", Charsets.UTF_8);
try
{
final JSONObject head = new JSONObject(page.toString());
final String headErr = head.optString("err", null);
if (headErr != null)
throw new RuntimeException(headErr);
final ResultHeader header = new ResultHeader(network, SERVER_PRODUCT, head.getString("ver"), 0, null);
final JSONArray svcResList = head.getJSONArray("svcResL");
checkState(svcResList.length() == 1);
final JSONObject svcRes = svcResList.optJSONObject(0);
checkState("LocGeoPos".equals(svcRes.getString("meth")));
final String err = svcRes.getString("err");
if (!"OK".equals(err))
{
final String errTxt = svcRes.getString("errTxt");
throw new RuntimeException(err + ": " + errTxt);
}
final JSONObject res = svcRes.getJSONObject("res");
final JSONObject common = res.getJSONObject("common");
/* final List<String[]> remarks = */ parseRemList(common.getJSONArray("remL"));
final JSONArray locL = res.optJSONArray("locL");
final List<Location> locations;
if (locL != null)
{
locations = parseLocList(locL);
// filter unwanted location types
for (Iterator<Location> i = locations.iterator(); i.hasNext();)
{
final Location location = i.next();
if (!types.contains(location.type))
i.remove();
}
}
else
{
locations = Collections.emptyList();
}
return new NearbyLocationsResult(header, locations);
}
catch (final JSONException x)
{
throw new ParserException("cannot parse json: '" + page + "' on " + uri, x);
}
}
protected final QueryDeparturesResult jsonStationBoard(final String stationId, final @Nullable Date time, final int maxDepartures,
final boolean equivs) throws IOException
{
final Calendar c = new GregorianCalendar(timeZone);
c.setTime(time);
final CharSequence jsonDate = jsonDate(c);
final CharSequence jsonTime = jsonTime(c);
final CharSequence normalizedStationId = normalizeStationId(stationId);
final CharSequence stbFltrEquiv = Boolean.toString(!equivs);
final CharSequence maxJny = Integer.toString(maxDepartures != 0 ? maxDepartures : DEFAULT_MAX_DEPARTURES);
final CharSequence getPasslist = Boolean.toString(true); // traffic expensive
final String request = wrapJsonApiRequest("StationBoard", "{\"type\":\"DEP\"," //
+ "\"date\":\"" + jsonDate + "\"," //
+ "\"time\":\"" + jsonTime + "\"," //
+ "\"stbLoc\":{\"type\":\"S\"," + "\"state\":\"F\"," // F/M
+ "\"extId\":" + JSONObject.quote(normalizedStationId.toString()) + "}," //
+ "\"stbFltrEquiv\":" + stbFltrEquiv + ",\"maxJny\":" + maxJny + ",\"getPasslist\":" + getPasslist + "}", false);
final String uri = checkNotNull(mgateEndpoint);
final CharSequence page = httpClient.get(uri, request, "application/json", Charsets.UTF_8);
try
{
final JSONObject head = new JSONObject(page.toString());
final String headErr = head.optString("err", null);
if (headErr != null)
throw new RuntimeException(headErr);
final ResultHeader header = new ResultHeader(network, SERVER_PRODUCT, head.getString("ver"), 0, null);
final QueryDeparturesResult result = new QueryDeparturesResult(header);
final JSONArray svcResList = head.getJSONArray("svcResL");
checkState(svcResList.length() == 1);
final JSONObject svcRes = svcResList.optJSONObject(0);
checkState("StationBoard".equals(svcRes.getString("meth")));
final String err = svcRes.getString("err");
if (!"OK".equals(err))
{
final String errTxt = svcRes.getString("errTxt");
if ("LOCATION".equals(err) && "HCI Service: location missing or invalid".equals(errTxt))
return new QueryDeparturesResult(header, QueryDeparturesResult.Status.INVALID_STATION);
else
throw new RuntimeException(err + ": " + errTxt);
}
final JSONObject res = svcRes.getJSONObject("res");
final JSONObject common = res.getJSONObject("common");
/* final List<String[]> remarks = */ parseRemList(common.getJSONArray("remL"));
final List<String> operators = parseOpList(common.getJSONArray("opL"));
final List<Line> lines = parseProdList(common.getJSONArray("prodL"), operators);
final JSONArray locList = common.getJSONArray("locL");
final List<Location> locations = parseLocList(locList);
final JSONArray jnyList = res.optJSONArray("jnyL");
if (jnyList != null)
{
for (int iJny = 0; iJny < jnyList.length(); iJny++)
{
final JSONObject jny = jnyList.getJSONObject(iJny);
final JSONObject stbStop = jny.getJSONObject("stbStop");
final String stbStopPlatformS = stbStop.optString("dPlatfS", null);
c.clear();
ParserUtils.parseIsoDate(c, jny.getString("date"));
final Date baseDate = c.getTime();
final Date plannedTime = parseJsonTime(c, baseDate, stbStop.getString("dTimeS"));
final Date predictedTime = parseJsonTime(c, baseDate, stbStop.optString("dTimeR", null));
final Line line = lines.get(stbStop.getInt("dProdX"));
final Location location = equivs ? locations.get(stbStop.getInt("locX")) : new Location(LocationType.STATION, stationId);
final Position position = normalizePosition(stbStopPlatformS);
final String jnyDirTxt = jny.getString("dirTxt");
final JSONArray stopList = jny.optJSONArray("stopL");
final Location destination;
if (stopList != null)
{
final int lastStopIdx = stopList.getJSONObject(stopList.length() - 1).getInt("locX");
final String lastStopName = locList.getJSONObject(lastStopIdx).getString("name");
if (jnyDirTxt.equals(lastStopName))
destination = locations.get(lastStopIdx);
else
destination = new Location(LocationType.ANY, null, null, jnyDirTxt);
}
else
{
destination = new Location(LocationType.ANY, null, null, jnyDirTxt);
}
final Departure departure = new Departure(plannedTime, predictedTime, line, position, destination, null, null);
StationDepartures stationDepartures = findStationDepartures(result.stationDepartures, location);
if (stationDepartures == null)
{
stationDepartures = new StationDepartures(location, new ArrayList<Departure>(8), null);
result.stationDepartures.add(stationDepartures);
}
stationDepartures.departures.add(departure);
}
}
// sort departures
for (final StationDepartures stationDepartures : result.stationDepartures)
Collections.sort(stationDepartures.departures, Departure.TIME_COMPARATOR);
return result;
}
catch (final JSONException x)
{
throw new ParserException("cannot parse json: '" + page + "' on " + uri, x);
}
}
protected final SuggestLocationsResult jsonLocMatch(final CharSequence constraint) throws IOException
{
final String request = wrapJsonApiRequest("LocMatch", "{\"input\":{\"field\":\"S\",\"loc\":{\"name\":"
+ JSONObject.quote(checkNotNull(constraint).toString()) + ",\"meta\":false},\"maxLoc\":" + DEFAULT_MAX_LOCATIONS + "}}", true);
final String uri = checkNotNull(mgateEndpoint);
final CharSequence page = httpClient.get(uri, request, "application/json", Charsets.UTF_8);
try
{
final JSONObject head = new JSONObject(page.toString());
final String headErr = head.optString("err", null);
if (headErr != null)
throw new RuntimeException(headErr);
final ResultHeader header = new ResultHeader(network, SERVER_PRODUCT, head.getString("ver"), 0, null);
final JSONArray svcResList = head.getJSONArray("svcResL");
checkState(svcResList.length() == 1);
final JSONObject svcRes = svcResList.optJSONObject(0);
checkState("LocMatch".equals(svcRes.getString("meth")));
final String err = svcRes.getString("err");
if (!"OK".equals(err))
{
final String errTxt = svcRes.getString("errTxt");
throw new RuntimeException(err + ": " + errTxt);
}
final JSONObject res = svcRes.getJSONObject("res");
final JSONObject common = res.getJSONObject("common");
/* final List<String[]> remarks = */ parseRemList(common.getJSONArray("remL"));
final JSONObject match = res.getJSONObject("match");
final List<Location> locations = parseLocList(match.optJSONArray("locL"));
final List<SuggestedLocation> suggestedLocations = new ArrayList<SuggestedLocation>(locations.size());
for (final Location location : locations)
suggestedLocations.add(new SuggestedLocation(location));
// TODO weight
return new SuggestLocationsResult(header, suggestedLocations);
}
catch (final JSONException x)
{
throw new ParserException("cannot parse json: '" + page + "' on " + uri, x);
}
}
protected final QueryTripsResult jsonTripSearch(final Location from, final Location to, final Date time, final boolean dep,
final @Nullable Set<Product> products, final String moreContext) throws IOException
{
final Calendar c = new GregorianCalendar(timeZone);
c.setTime(time);
final CharSequence outDate = jsonDate(c);
final CharSequence outTime = jsonTime(c);
final CharSequence outFrwd = Boolean.toString(dep);
final CharSequence jnyFltr = productsString(products);
final CharSequence jsonContext = moreContext != null ? "\"ctxScr\":" + JSONObject.quote(moreContext) + "," : "";
final String request = wrapJsonApiRequest("TripSearch", "{" //
+ jsonContext //
+ "\"depLocL\":[" + jsonLocation(from) + "]," //
+ "\"arrLocL\":[" + jsonLocation(to) + "]," //
+ "\"outDate\":\"" + outDate + "\"," //
+ "\"outTime\":\"" + outTime + "\"," //
+ "\"outFrwd\":" + outFrwd + "," //
+ "\"jnyFltrL\":[{\"value\":\"" + jnyFltr + "\",\"mode\":\"BIT\",\"type\":\"PROD\"}]," //
+ "\"gisFltrL\":[{\"mode\":\"FB\",\"profile\":{\"type\":\"F\",\"linDistRouting\":false,\"maxdist\":2000},\"type\":\"P\"}]," //
+ "\"getPolyline\":false,\"getPasslist\":true,\"liveSearch\":false,\"getIST\":false,\"getEco\":false,\"extChgTime\":-1,\"economic\":false}", //
false);
final String uri = checkNotNull(mgateEndpoint);
final CharSequence page = httpClient.get(uri, request, "application/json", Charsets.UTF_8);
try
{
final JSONObject head = new JSONObject(page.toString());
final String headErr = head.optString("err", null);
if (headErr != null)
throw new RuntimeException(headErr);
final ResultHeader header = new ResultHeader(network, SERVER_PRODUCT, head.getString("ver"), 0, null);
final JSONArray svcResList = head.getJSONArray("svcResL");
checkState(svcResList.length() == 1);
final JSONObject svcRes = svcResList.optJSONObject(0);
checkState("TripSearch".equals(svcRes.getString("meth")));
final String err = svcRes.getString("err");
if (!"OK".equals(err))
{
if ("H890".equals(err)) // No connections found.
return new QueryTripsResult(header, QueryTripsResult.Status.NO_TRIPS);
if ("H895".equals(err)) // Departure/Arrival are too near.
return new QueryTripsResult(header, QueryTripsResult.Status.TOO_CLOSE);
if ("H9220".equals(err)) // Nearby to the given address stations could not be found.
return new QueryTripsResult(header, QueryTripsResult.Status.UNRESOLVABLE_ADDRESS);
final String errTxt = svcRes.getString("errTxt");
throw new RuntimeException(err + ": " + errTxt);
}
final JSONObject res = svcRes.getJSONObject("res");
final JSONObject common = res.getJSONObject("common");
/* final List<String[]> remarks = */ parseRemList(common.getJSONArray("remL"));
final List<Location> locations = parseLocList(common.getJSONArray("locL"));
final List<String> operators = parseOpList(common.getJSONArray("opL"));
final List<Line> lines = parseProdList(common.getJSONArray("prodL"), operators);
final JSONArray outConList = res.optJSONArray("outConL");
final List<Trip> trips = new ArrayList<Trip>(outConList.length());
for (int iOutCon = 0; iOutCon < outConList.length(); iOutCon++)
{
final JSONObject outCon = outConList.getJSONObject(iOutCon);
final Location tripFrom = locations.get(outCon.getJSONObject("dep").getInt("locX"));
final Location tripTo = locations.get(outCon.getJSONObject("arr").getInt("locX"));
c.clear();
ParserUtils.parseIsoDate(c, outCon.getString("date"));
final Date baseDate = c.getTime();
final JSONArray secList = outCon.optJSONArray("secL");
final List<Trip.Leg> legs = new ArrayList<Trip.Leg>(secList.length());
for (int iSec = 0; iSec < secList.length(); iSec++)
{
final JSONObject sec = secList.getJSONObject(iSec);
final String secType = sec.getString("type");
final JSONObject secDep = sec.getJSONObject("dep");
final Stop departureStop = parseJsonStop(secDep, locations, c, baseDate);
final JSONObject secArr = sec.getJSONObject("arr");
final Stop arrivalStop = parseJsonStop(secArr, locations, c, baseDate);
final Trip.Leg leg;
if ("JNY".equals(secType))
{
final JSONObject jny = sec.getJSONObject("jny");
final Line line = lines.get(jny.getInt("prodX"));
final String dirTxt = jny.optString("dirTxt", null);
final Location destination = dirTxt != null ? new Location(LocationType.ANY, null, null, dirTxt) : null;
final JSONArray stopList = jny.getJSONArray("stopL");
checkState(stopList.length() >= 2);
final List<Stop> intermediateStops = new ArrayList<Stop>(stopList.length());
for (int iStop = 1; iStop < stopList.length() - 1; iStop++)
{
final JSONObject stop = stopList.getJSONObject(iStop);
final Stop intermediateStop = parseJsonStop(stop, locations, c, baseDate);
intermediateStops.add(intermediateStop);
}
leg = new Trip.Public(line, destination, departureStop, arrivalStop, intermediateStops, null, null);
}
else if ("WALK".equals(secType) || "TRSF".equals(secType))
{
final JSONObject gis = sec.getJSONObject("gis");
final int distance = gis.getInt("dist");
leg = new Trip.Individual(Trip.Individual.Type.WALK, departureStop.location, departureStop.getDepartureTime(),
arrivalStop.location, arrivalStop.getArrivalTime(), null, distance);
}
else
{
throw new IllegalStateException("cannot handle type: " + secType);
}
legs.add(leg);
}
final JSONObject trfRes = outCon.optJSONObject("trfRes");
final List<Fare> fares = new LinkedList<Fare>();
if (trfRes != null)
{
final JSONArray fareSetList = trfRes.getJSONArray("fareSetL");
for (int iFareSet = 0; iFareSet < fareSetList.length(); iFareSet++)
{
final JSONObject fareSet = fareSetList.getJSONObject(iFareSet);
final String network = fareSet.optString("name", null);
if (network != null)
{
final JSONArray fareList = fareSet.getJSONArray("fareL");
for (int iFare = 0; iFare < fareList.length(); iFare++)
{
final JSONObject jsonFare = fareList.getJSONObject(iFare);
final String name = jsonFare.getString("name");
if (name.endsWith("- Jahreskarte") || name.endsWith("- Monatskarte"))
continue;
final Currency currency = Currency.getInstance(jsonFare.getString("cur"));
final float price = jsonFare.getInt("prc") / 100f;
if (name.startsWith("Vollpreis - "))
fares.add(new Fare(network, Fare.Type.ADULT, currency, price, name.substring(12), null));
else if (name.startsWith("Kind - "))
fares.add(new Fare(network, Fare.Type.CHILD, currency, price, name.substring(7), null));
}
}
}
}
final Trip trip = new Trip(null, tripFrom, tripTo, legs, fares, null, null);
trips.add(trip);
}
final JsonContext context = new JsonContext(from, to, time, dep, products, res.getString("outCtxScrF"), res.getString("outCtxScrB"));
return new QueryTripsResult(header, null, from, null, to, context, trips);
}
catch (final JSONException x)
{
throw new ParserException("cannot parse json: '" + page + "' on " + uri, x);
}
}
private String wrapJsonApiRequest(final String meth, final String req, final boolean formatted)
{
return "{" //
+ "\"auth\":" + checkNotNull(jsonApiAuthorization) + "," //
+ "\"client\":" + checkNotNull(jsonApiClient) + "," //
+ "\"ver\":\"" + checkNotNull(jsonApiVersion) + "\",\"lang\":\"eng\"," //
+ "\"svcReqL\":[{\"cfg\":{\"polyEnc\":\"GPA\"},\"meth\":\"" + meth + "\",\"req\":" + req + "}]," //
+ "\"formatted\":" + formatted + "}";
}
private String jsonLocation(final Location location)
{
if (location.type == LocationType.STATION && location.hasId())
return "{\"type\":\"S\",\"extId\":" + JSONObject.quote(location.id) + "}";
else if (location.type == LocationType.ADDRESS && location.hasId())
return "{\"type\":\"A\",\"lid\":" + JSONObject.quote(location.id) + "}";
else
throw new IllegalArgumentException("cannot handle: " + location);
}
private CharSequence jsonDate(final Calendar time)
{
final int year = time.get(Calendar.YEAR);
final int month = time.get(Calendar.MONTH) + 1;
final int day = time.get(Calendar.DAY_OF_MONTH);
return String.format(Locale.ENGLISH, "%04d%02d%02d", year, month, day);
}
private CharSequence jsonTime(final Calendar time)
{
final int hour = time.get(Calendar.HOUR_OF_DAY);
final int minute = time.get(Calendar.MINUTE);
return String.format(Locale.ENGLISH, "%02d%02d00", hour, minute);
}
private static final Pattern P_JSON_TIME = Pattern.compile("(\\d{2})?(\\d{2})(\\d{2})(\\d{2})");
private final Date parseJsonTime(final Calendar calendar, final Date baseDate, final CharSequence str)
{
if (str == null)
return null;
final Matcher m = P_JSON_TIME.matcher(str);
if (m.matches())
{
calendar.setTime(baseDate);
if (m.group(1) != null)
calendar.add(Calendar.DAY_OF_YEAR, Integer.parseInt(m.group(1)));
calendar.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(2)));
calendar.set(Calendar.MINUTE, Integer.parseInt(m.group(3)));
calendar.set(Calendar.SECOND, Integer.parseInt(m.group(4)));
return calendar.getTime();
}
throw new RuntimeException("cannot parse: '" + str + "'");
}
private Stop parseJsonStop(final JSONObject json, final List<Location> locations, final Calendar c, final Date baseDate) throws JSONException
{
final Location location = locations.get(json.getInt("locX"));
final boolean arrivalCancelled = json.optBoolean("aCncl", false);
final Date plannedArrivalTime = parseJsonTime(c, baseDate, json.optString("aTimeS", null));
final Date predictedArrivalTime = parseJsonTime(c, baseDate, json.optString("aTimeR", null));
final Position plannedArrivalPosition = normalizePosition(json.optString("aPlatfS", null));
final Position predictedArrivalPosition = normalizePosition(json.optString("aPlatfR", null));
final boolean departureCancelled = json.optBoolean("dCncl", false);
final Date plannedDepartureTime = parseJsonTime(c, baseDate, json.optString("dTimeS", null));
final Date predictedDepartureTime = parseJsonTime(c, baseDate, json.optString("dTimeR", null));
final Position plannedDeparturePosition = normalizePosition(json.optString("dPlatfS", null));
final Position predictedDeparturePosition = normalizePosition(json.optString("dPlatfR", null));
return new Stop(location, plannedArrivalTime, predictedArrivalTime, plannedArrivalPosition, predictedArrivalPosition, arrivalCancelled,
plannedDepartureTime, predictedDepartureTime, plannedDeparturePosition, predictedDeparturePosition, departureCancelled);
}
private List<String[]> parseRemList(final JSONArray remList) throws JSONException
{
final List<String[]> remarks = new ArrayList<String[]>(remList.length());
for (int i = 0; i < remList.length(); i++)
{
final JSONObject rem = remList.getJSONObject(i);
final String code = rem.getString("code");
final String txt = rem.getString("txtN");
remarks.add(new String[] { code, txt });
}
return remarks;
}
private List<Location> parseLocList(final JSONArray locList) throws JSONException
{
final List<Location> locations = new ArrayList<Location>(locList.length());
for (int iLoc = 0; iLoc < locList.length(); iLoc++)
{
final JSONObject loc = locList.getJSONObject(iLoc);
final String type = loc.getString("type");
final JSONObject crd = loc.getJSONObject("crd");
if ("S".equals(type))
{
final String[] placeAndName = splitStationName(loc.getString("name"));
final int pCls = loc.optInt("pCls", -1);
final Set<Product> products = pCls != -1 ? intToProducts(pCls) : null;
final String id = normalizeStationId(loc.getString("extId"));
locations.add(new Location(LocationType.STATION, id, crd.getInt("y"), crd.getInt("x"), placeAndName[0], placeAndName[1], products));
}
else if ("P".equals(type))
{
final String[] placeAndName = splitPOI(loc.getString("name"));
final String id = normalizeStationId(loc.getString("extId"));
locations.add(new Location(LocationType.POI, id, crd.getInt("y"), crd.getInt("x"), placeAndName[0], placeAndName[1]));
}
else if ("A".equals(type))
{
final String[] placeAndName = splitAddress(loc.getString("name"));
final String id = loc.getString("lid");
locations.add(new Location(LocationType.ADDRESS, id, crd.getInt("y"), crd.getInt("x"), placeAndName[0], placeAndName[1]));
}
else
{
throw new RuntimeException("Unknown type " + type + ": " + loc);
}
}
return locations;
}
private List<String> parseOpList(final JSONArray opList) throws JSONException
{
final List<String> operators = new ArrayList<String>(opList.length());
for (int i = 0; i < opList.length(); i++)
{
final JSONObject op = opList.getJSONObject(i);
final String operator = op.getString("name");
operators.add(operator);
}
return operators;
}
private List<Line> parseProdList(final JSONArray prodList, final List<String> operators) throws JSONException
{
final List<Line> lines = new ArrayList<Line>(prodList.length());
for (int iProd = 0; iProd < prodList.length(); iProd++)
{
final JSONObject prod = prodList.getJSONObject(iProd);
final int oprIndex = prod.optInt("oprX", -1);
final String operator = oprIndex != -1 ? operators.get(oprIndex) : null;
final int cls = prod.optInt("cls", -1);
final Product product = cls != -1 ? intToProduct(cls) : null;
final String name = prod.getString("name");
final Line line = new Line(null, operator, product, name, lineStyle(operator, product, name));
lines.add(line);
}
return lines;
}
public QueryTripsResult queryTrips(final Location from, final @Nullable Location via, final Location to, final Date date, final boolean dep,
final @Nullable Set<Product> products, final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) throws IOException

View file

@ -29,7 +29,7 @@ public enum NetworkId
DB, BVG, VBB, NVV, BAYERN, MVV, INVG, AVV, VGN, VVM, VMV, RSAG, HVV, SH, GVH, VSN, BSVAG, VBN, NASA, VVO, VMS, VGS, VRR, VRS, MVG, NPH, VRN, VVS, DING, KVV, VAGFR, NVBW, VVV,
// Austria
OEBB, VOR, WIEN, LINZ, SVV, VVT, VMOBIL, IVB, STV,
OEBB, VAO, VOR, WIEN, LINZ, SVV, VVT, IVB, STV,
// Switzerland
SBB, BVB, VBL, ZVV,

View file

@ -0,0 +1,136 @@
/*
* Copyright 2015 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.Date;
import java.util.EnumSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import com.google.common.base.Charsets;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryDeparturesResult;
import de.schildbach.pte.dto.QueryTripsContext;
import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult;
/**
* @author Andreas Schildbach
*/
public class VaoProvider extends AbstractHafasProvider
{
private static final String API_BASE = "http://app.verkehrsauskunft.at/bin/";
private static final Product[] PRODUCTS_MAP = { Product.HIGH_SPEED_TRAIN, Product.SUBURBAN_TRAIN, Product.SUBWAY, null, Product.TRAM,
Product.REGIONAL_TRAIN, Product.BUS, Product.BUS, Product.TRAM, Product.FERRY, Product.ON_DEMAND, Product.BUS, Product.REGIONAL_TRAIN,
null, null, null };
public VaoProvider(final String jsonApiAuthorization)
{
super(NetworkId.VAO, API_BASE, "dn", PRODUCTS_MAP);
setJsonApiVersion("1.11");
setJsonApiClient("{\"id\":\"VAO\",\"l\":\"vs_vvv\"}");
setJsonApiAuthorization(jsonApiAuthorization);
setJsonNearbyLocationsEncoding(Charsets.UTF_8);
}
@Override
public Set<Product> defaultProducts()
{
return Product.ALL;
}
private static final Pattern P_SPLIT_NAME_ONE_COMMA = Pattern.compile("([^,]*), ([^,]{3,64})");
@Override
protected String[] splitStationName(final String name)
{
final Matcher m = P_SPLIT_NAME_ONE_COMMA.matcher(name);
if (m.matches())
return new String[] { m.group(2), m.group(1) };
return super.splitStationName(name);
}
@Override
protected String[] splitPOI(final String poi)
{
final Matcher m = P_SPLIT_NAME_ONE_COMMA.matcher(poi);
if (m.matches())
return new String[] { m.group(2), m.group(1) };
return super.splitPOI(poi);
}
@Override
protected String[] splitAddress(final String address)
{
final Matcher m = P_SPLIT_NAME_FIRST_COMMA.matcher(address);
if (m.matches())
return new String[] { m.group(1), m.group(2) };
return super.splitAddress(address);
}
@Override
public NearbyLocationsResult queryNearbyLocations(final EnumSet<LocationType> types, final Location location, final int maxDistance,
final int maxLocations) throws IOException
{
if (location.hasLocation())
return jsonLocGeoPos(types, location.lat, location.lon);
else
throw new IllegalArgumentException("cannot handle: " + location);
}
@Override
public QueryDeparturesResult queryDepartures(final String stationId, final @Nullable Date time, final int maxDepartures, final boolean equivs)
throws IOException
{
return jsonStationBoard(stationId, time, maxDepartures, equivs);
}
@Override
public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException
{
return jsonLocMatch(constraint);
}
@Override
public QueryTripsResult queryTrips(final Location from, final @Nullable Location via, final Location to, final Date date, final boolean dep,
final @Nullable Set<Product> products, final @Nullable Optimize optimize, final @Nullable WalkSpeed walkSpeed,
final @Nullable Accessibility accessibility, final @Nullable Set<Option> options) throws IOException
{
return jsonTripSearch(from, to, date, dep, products, null);
}
@Override
public QueryTripsResult queryMoreTrips(final QueryTripsContext context, final boolean later) throws IOException
{
final JsonContext jsonContext = (JsonContext) context;
return jsonTripSearch(jsonContext.from, jsonContext.to, jsonContext.date, jsonContext.dep, jsonContext.products,
later ? jsonContext.laterContext : jsonContext.earlierContext);
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright 2010-2015 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;
/**
* Verkehrsverbund Vorarlberg
*
* @author Andreas Schildbach
*/
public class VmobilProvider extends AbstractEfaProvider
{
private final static String API_BASE = "http://efaneu.vmobil.at/vvvmobile/";
public VmobilProvider()
{
super(NetworkId.VMOBIL, API_BASE);
}
}

View file

@ -24,4 +24,5 @@ public final class Secrets
{
public static final String VGN_API_BASE = "insert vgn base here";
public static final String NAVITIA_AUTHORIZATION = "insert navitia authorization here";
public static final String VAO_JSON_API_AUTHORIZATION = "insert vao authorization here";
}

View file

@ -0,0 +1,153 @@
/*
* Copyright 2015 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 static org.junit.Assert.assertEquals;
import java.util.Date;
import org.junit.Test;
import de.schildbach.pte.NetworkProvider.Accessibility;
import de.schildbach.pte.NetworkProvider.WalkSpeed;
import de.schildbach.pte.VaoProvider;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryDeparturesResult;
import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult;
/**
* @author Andreas Schildbach
*/
public class VaoProviderLiveTest extends AbstractProviderLiveTest
{
public VaoProviderLiveTest()
{
super(new VaoProvider(Secrets.VAO_JSON_API_AUTHORIZATION));
}
@Test
public void nearbyStations() throws Exception
{
final NearbyLocationsResult result = queryNearbyStations(new Location(LocationType.STATION, "490132007"));
print(result);
}
@Test
public void nearbyStationsByCoordinate() throws Exception
{
final NearbyLocationsResult result = queryNearbyStations(Location.coord(48207355, 16370602));
print(result);
}
@Test
public void queryDepartures() throws Exception
{
final QueryDeparturesResult result = queryDepartures("480082200", 0, false);
print(result);
assertEquals(QueryDeparturesResult.Status.OK, result.status);
}
@Test
public void queryDeparturesInvalidStation() throws Exception
{
final QueryDeparturesResult result = queryDepartures("999999", 0, false);
assertEquals(QueryDeparturesResult.Status.INVALID_STATION, result.status);
}
@Test
public void suggestLocations() throws Exception
{
final SuggestLocationsResult result = suggestLocations("Katzenturm");
print(result);
}
@Test
public void suggestLocationsAddress() throws Exception
{
final SuggestLocationsResult result = suggestLocations("Mutterstraße 4, 6800 Feldkirch");
print(result);
}
@Test
public void suggestLocationsEncoding() throws Exception
{
final SuggestLocationsResult result = suggestLocations("Schönbrunn");
assertEquals("Schönbrunn", result.getLocations().get(0).name);
print(result);
}
@Test
public void shortTripFeldkirch() throws Exception
{
final QueryTripsResult result = queryTrips(new Location(LocationType.STATION, "480082200", null, "Feldkirch Katzenturm"), null,
new Location(LocationType.STATION, "480081700", null, "Feldkirch Bahnhof"), new Date(), true, Product.ALL, WalkSpeed.NORMAL,
Accessibility.NEUTRAL);
print(result);
final QueryTripsResult laterResult = queryMoreTrips(result.context, true);
print(laterResult);
final QueryTripsResult laterResult2 = queryMoreTrips(laterResult.context, true);
print(laterResult2);
final QueryTripsResult earlierResult = queryMoreTrips(result.context, false);
print(earlierResult);
}
@Test
public void shortTripWien() throws Exception
{
final QueryTripsResult result = queryTrips(new Location(LocationType.STATION, "490132000", null, "Wien Stephansplatz"), null,
new Location(LocationType.STATION, "490024500", null, "Wien Stubentor"), new Date(), true, Product.ALL, WalkSpeed.NORMAL,
Accessibility.NEUTRAL);
print(result);
final QueryTripsResult laterResult = queryMoreTrips(result.context, true);
print(laterResult);
final QueryTripsResult laterResult2 = queryMoreTrips(laterResult.context, true);
print(laterResult2);
final QueryTripsResult earlierResult = queryMoreTrips(result.context, false);
print(earlierResult);
}
@Test
public void tripAddressToStation() throws Exception
{
final QueryTripsResult result = queryTrips(
new Location(LocationType.ADDRESS, "A=2@O=6800 Feldkirch, Kapfweg 6@X=9585539@Y=47239257@U=103@L=980092305@B=1@p=1437727591@",
"6800 Feldkirch", "Kapfweg 6"),
null, new Location(LocationType.STATION, "480081700", null, "Feldkirch Bahnhof"), new Date(), true, Product.ALL, WalkSpeed.NORMAL,
Accessibility.NEUTRAL);
print(result);
}
@Test
public void tripCoordinateToStation() throws Exception
{
final QueryTripsResult result = queryTrips(Location.coord(47238096, 9585581), null,
new Location(LocationType.STATION, "480081700", null, "Feldkirch Bahnhof"), new Date(), true, Product.ALL, WalkSpeed.NORMAL,
Accessibility.NEUTRAL);
print(result);
}
}

View file

@ -1,118 +0,0 @@
/*
* Copyright 2010-2015 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Date;
import org.junit.Test;
import de.schildbach.pte.NetworkProvider.Accessibility;
import de.schildbach.pte.NetworkProvider.WalkSpeed;
import de.schildbach.pte.VmobilProvider;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryDeparturesResult;
import de.schildbach.pte.dto.QueryTripsResult;
import de.schildbach.pte.dto.SuggestLocationsResult;
/**
* @author Andreas Schildbach
*/
public class VmobilProviderLiveTest extends AbstractProviderLiveTest
{
public VmobilProviderLiveTest()
{
super(new VmobilProvider());
}
@Test
public void nearbyStations() throws Exception
{
final NearbyLocationsResult result = queryNearbyStations(new Location(LocationType.STATION, "60001296"));
print(result);
}
@Test
public void nearbyStationsByCoordinate() throws Exception
{
final NearbyLocationsResult result = queryNearbyStations(Location.coord(47271228, 11402063));
print(result);
}
@Test
public void queryDepartures() throws Exception
{
final QueryDeparturesResult result = queryDepartures("60001296", false);
print(result);
}
@Test
public void suggestLocationsIdentified() throws Exception
{
final SuggestLocationsResult result = suggestLocations("Katzenturm");
print(result);
}
@Test
public void suggestLocationsIncomplete() throws Exception
{
final SuggestLocationsResult result = suggestLocations("Kur");
print(result);
}
@Test
public void suggestLocationsWithUmlaut() throws Exception
{
final SuggestLocationsResult result = suggestLocations("grün");
print(result);
}
@Test
public void shortTrip() throws Exception
{
final QueryTripsResult result = queryTrips(new Location(LocationType.STATION, "66200822", 47238428, 9596940, "Feldkirch", "Katzenturm"),
null, new Location(LocationType.STATION, "66200305", 47240744, 9589368, "Tosters", "Vorarlberghalle"), new Date(), true, Product.ALL,
WalkSpeed.NORMAL, Accessibility.NEUTRAL);
print(result);
assertEquals(QueryTripsResult.Status.OK, result.status);
assertTrue(result.trips.size() > 0);
if (!result.context.canQueryLater())
return;
final QueryTripsResult laterResult = queryMoreTrips(result.context, true);
print(laterResult);
if (!laterResult.context.canQueryLater())
return;
final QueryTripsResult later2Result = queryMoreTrips(laterResult.context, true);
print(later2Result);
if (!later2Result.context.canQueryEarlier())
return;
final QueryTripsResult earlierResult = queryMoreTrips(later2Result.context, false);
print(earlierResult);
}
}