Provence-Alpes-Côte d’Azur.

This commit is contained in:
Kjell Braden 2014-02-18 15:05:32 +01:00 committed by Andreas Schildbach
parent 172e0590d6
commit 069686c746
4 changed files with 1070 additions and 0 deletions

View file

@ -0,0 +1,813 @@
/*
* Copyright 2014 Kjell Braden <afflux@pentabarf.de>
*
* 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
* 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.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyStationsResult;
import de.schildbach.pte.dto.Point;
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.ResultHeader;
import de.schildbach.pte.dto.Stop;
import de.schildbach.pte.dto.Trip;
import de.schildbach.pte.exception.ParserException;
import de.schildbach.pte.util.ParserUtils;
/**
* @author Kjell Braden <afflux@pentabarf.de>
*/
public abstract class AbstractTsiProvider extends AbstractNetworkProvider
{
private static class Context implements QueryTripsContext
{
private static final long serialVersionUID = -6847355540229473013L;
public final Accessibility accessibility;
public final Location from;
public final Set<Option> options;
public final Collection<Product> products;
public final Location to;
public final Location via;
public final WalkSpeed walkSpeed;
public Date earliestArrival;
public Date latestDeparture;
public Context(final Location from, final Location via, final Location to, final Collection<Product> products, final WalkSpeed walkSpeed,
final Accessibility accessibility, final Set<Option> options)
{
this.from = from;
this.via = via;
this.to = to;
this.products = products;
this.walkSpeed = walkSpeed;
this.accessibility = accessibility;
this.options = options;
}
public boolean canQueryEarlier()
{
return true;
}
public boolean canQueryLater()
{
return true;
}
private QueryTripsResult queryMore(final AbstractTsiProvider provider, final boolean later) throws IOException
{
final Date date = later ? latestDeparture : earliestArrival;
// add/remove a little bit just to be on the safe side
final Calendar c = Calendar.getInstance(provider.timeZone());
c.setTime(date);
c.add(Calendar.MINUTE, later ? 1 : -1);
return provider.queryTripsFromContext(this, c.getTime(), later);
}
private void updateEarliestArrival(final Date newTime)
{
if (newTime == null)
return;
if (earliestArrival == null || newTime.before(earliestArrival))
earliestArrival = newTime;
}
private void updateLatestDeparture(final Date newTime)
{
if (newTime == null)
return;
if (latestDeparture == null || newTime.after(latestDeparture))
latestDeparture = newTime;
}
}
private static final String DEFAULT_STOPFINDER_ENDPOINT = "/Transport/v2/";
private static final String DEFAULT_TRIP_ENDPOINT = "/journeyplanner/v2/";
private static final ResultHeader HEADER = new ResultHeader("tsi");
private static Map<String, Character> TRANSPORT_MODE_SHORTS = new HashMap<String, Character>();
static
{
// HIGH_SPEED_TRAIN
TRANSPORT_MODE_SHORTS.put("TGV", 'I');
TRANSPORT_MODE_SHORTS.put("HST", 'I');
// REGIONAL_TRAIN
TRANSPORT_MODE_SHORTS.put("TRAIN", 'R');
TRANSPORT_MODE_SHORTS.put("TER", 'R');
// SUBURBAN_TRAIN
TRANSPORT_MODE_SHORTS.put("LOCAL_TRAIN", 'S');
// SUBWAY
TRANSPORT_MODE_SHORTS.put("METRO", 'U');
// TRAM
TRANSPORT_MODE_SHORTS.put("TRAM", 'T');
TRANSPORT_MODE_SHORTS.put("TRAMWAY", 'T');
// BUS
TRANSPORT_MODE_SHORTS.put("BUS", 'B');
TRANSPORT_MODE_SHORTS.put("COACH", 'B');
// CABLECAR
TRANSPORT_MODE_SHORTS.put("TROLLEY", 'C');
TRANSPORT_MODE_SHORTS.put("TROLLEY_BUS", 'C');
}
protected static double latLonToDouble(final int value)
{
return (double) value / 1000000;
}
private final String apiKey;
private final String stopFinderEndpoint;
private final String tripEndpoint;
public AbstractTsiProvider(final String apiKey, final String apiBase)
{
this(apiKey, apiBase, null, null);
}
public AbstractTsiProvider(final String apiKey, final String tripEndpoint, final String stopFinderEndpoint)
{
this.apiKey = apiKey;
this.tripEndpoint = tripEndpoint;
this.stopFinderEndpoint = stopFinderEndpoint;
}
public AbstractTsiProvider(final String apiKey, final String apiBase, final String tripEndpoint, final String stopFinderEndpoint)
{
this(apiKey, apiBase + (tripEndpoint != null ? tripEndpoint : DEFAULT_TRIP_ENDPOINT), //
apiBase + (stopFinderEndpoint != null ? stopFinderEndpoint : DEFAULT_STOPFINDER_ENDPOINT));
}
public List<Location> autocompleteStations(final CharSequence constraint) throws IOException
{
final StringBuilder parameters = buildCommonRequestParams("SearchTripPoint", "json");
parameters.append("&MaxItems=").append(50); // XXX good value?
parameters.append("&Keywords=").append(ParserUtils.urlEncode(constraint.toString(), UTF_8));
final StringBuilder uri = new StringBuilder(stopFinderEndpoint);
uri.append(parameters);
final CharSequence page = ParserUtils.scrape(uri.toString(), null, UTF_8, null, 3);
try
{
final List<Location> stations = new ArrayList<Location>();
final JSONObject head = new JSONObject(page.toString());
int status = head.getInt("StatusCode");
if (status != 200)
return stations;
JSONArray dataArray = head.getJSONArray("Data");
for (int i = 0; i < dataArray.length(); i++)
{
JSONObject data = dataArray.getJSONObject(i);
final Location location = parseJsonTransportLocation(data);
if (location.isIdentified()) // make sure the location is really identified
// some addresses may not contain coordinates, we ignore them
stations.add(location);
}
return stations;
}
catch (final JSONException x)
{
throw new ParserException(x);
}
}
private final StringBuilder buildCommonRequestParams(final String method, final String outputFormat)
{
final StringBuilder uri = new StringBuilder(method);
uri.append('/').append(outputFormat);
uri.append("?Key=").append(apiKey);
return uri;
}
private String createLineLabel(final String mode, final String number, final String name, final String operatorCode, final String codeActivity)
{
final Character modePrefix = TRANSPORT_MODE_SHORTS.get(mode);
if (modePrefix == null)
throw new IllegalStateException("cannot normalize mode '" + mode + "' number '" + number + "'");
final StringBuilder label = new StringBuilder(modePrefix);
if (number != null && !number.isEmpty())
{
label.append(number);
}
else if (operatorCode != null)
{
label.append(operatorCode);
if (codeActivity != null)
label.append(codeActivity);
}
else
{
label.append(name);
}
return label.toString();
}
private List<Location> identifyLocation(final Location location) throws IOException
{
if (location.isIdentified())
return Collections.singletonList(location);
final List<Location> result = autocompleteStations(location.uniqueShortName());
if (result == null)
return new ArrayList<Location>(0);
return result;
}
private NearbyStationsResult jsonCoordRequest(final int lat, final int lon, final int maxDistance, final int maxStations) throws IOException
{
final StringBuilder parameters = buildCommonRequestParams("SearchTripPoint", "json");
parameters.append(String.format(Locale.FRENCH, "&Latitude=%2.6f&Longitude=%2.6f", latLonToDouble(lat), latLonToDouble(lon)));
parameters.append("&MaxItems=").append(maxStations != 0 ? maxStations : 50);
parameters.append("&Perimeter=").append(maxDistance != 0 ? maxDistance : 1320);
parameters.append("&PointTypes=Stop_Place");
final StringBuilder uri = new StringBuilder(stopFinderEndpoint);
uri.append(parameters);
final CharSequence page = ParserUtils.scrape(uri.toString(), null, UTF_8, null, 3);
try
{
final List<Location> stations = new ArrayList<Location>();
final JSONObject head = new JSONObject(page.toString());
int status = head.getInt("StatusCode");
if (status != 200)
{
return new NearbyStationsResult(HEADER, status == 300 ? NearbyStationsResult.Status.INVALID_STATION
: NearbyStationsResult.Status.SERVICE_DOWN);
}
JSONArray dataArray = head.getJSONArray("Data");
for (int i = 0; i < dataArray.length(); i++)
{
JSONObject data = dataArray.getJSONObject(i);
stations.add(parseJsonTransportLocation(data));
}
return new NearbyStationsResult(HEADER, stations);
}
catch (final JSONException x)
{
throw new ParserException(x);
}
}
private String jsonOptString(final JSONObject json, final String key) throws JSONException
{
return json.isNull(key) ? null : json.getString(key);
}
private Location jsonStationRequestCoord(final int id) throws IOException
{
final StringBuilder parameters = buildCommonRequestParams("GetTripPoint", "json");
parameters.append("&TripPointId=").append(id);
parameters.append("&PointType=Stop_Place");
final StringBuilder uri = new StringBuilder(stopFinderEndpoint);
uri.append(parameters);
final CharSequence page = ParserUtils.scrape(uri.toString(), null, UTF_8, null, 3);
try
{
final JSONObject head = new JSONObject(page.toString());
int status = head.getInt("StatusCode");
if (status != 200)
return null;
JSONObject data = head.getJSONObject("Data");
return parseJsonTransportLocation(data);
}
catch (final JSONException x)
{
throw new ParserException(x);
}
}
private Trip.Individual parseJsonJourneyplannerIndividualLeg(final JSONObject legInfo) throws JSONException
{
final String transportMode = legInfo.getString("TransportMode");
final Trip.Individual.Type type;
if ("WALK".equals(transportMode))
{
type = Trip.Individual.Type.WALK;
}
else
{
throw new JSONException("unknown transportMode=" + transportMode);
}
final JSONObject arrInfo = legInfo.getJSONObject("Arrival");
final JSONObject depInfo = legInfo.getJSONObject("Departure");
final Location departure = parseJsonJourneyplannerLocation(depInfo.getJSONObject("Site"));
final Location arrival = parseJsonJourneyplannerLocation(arrInfo.getJSONObject("Site"));
final String arrTimeStr = arrInfo.getString("Time");
final String depTimeStr = depInfo.getString("Time");
final Date depTime, arrTime;
try
{
final DateFormat fullDateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.US);
depTime = fullDateFormat.parse(depTimeStr);
arrTime = fullDateFormat.parse(arrTimeStr);
}
catch (final ParseException e)
{
throw new JSONException("failed to parse trip times: " + depTimeStr + " or " + arrTimeStr);
}
final JSONArray pathLinks = legInfo.getJSONObject("pathLinks").getJSONArray("PathLink");
final List<Point> path = new ArrayList<Point>(pathLinks.length() + 1);
int distance = 0;
path.add(new Point(departure.lat, departure.lon));
for (int i = 0; i < pathLinks.length(); i++)
{
final JSONObject pathLink = pathLinks.getJSONObject(i);
distance += pathLink.getInt("Distance");
final Location toLoc = parseJsonJourneyplannerLocation(pathLink.getJSONObject("Arrival").getJSONObject("Site"));
path.add(new Point(toLoc.lat, toLoc.lon));
}
return new Trip.Individual(type, departure, depTime, arrival, arrTime, path, distance);
}
private Trip.Leg parseJsonJourneyplannerLeg(final JSONObject jsonObject) throws JSONException
{
final JSONObject legInfo = jsonObject.getJSONObject("Leg");
final JSONObject ptrInfo = jsonObject.getJSONObject("PTRide");
if (legInfo.isNull("TransportMode") && !ptrInfo.isNull("TransportMode"))
{
return parseJsonJourneyplannerPublicLeg(ptrInfo);
}
else if (!legInfo.isNull("TransportMode") && ptrInfo.isNull("TransportMode"))
{
return parseJsonJourneyplannerIndividualLeg(legInfo);
}
else
{
throw new JSONException("unknown leg type");
}
}
private Location parseJsonJourneyplannerLocation(final JSONObject data) throws JSONException
{
final String locTypeStr = data.getString("Type");
final LocationType locType;
final int id;
if ("POI".equals(locTypeStr))
{
locType = LocationType.POI;
id = data.getInt("id");
}
else if ("BOARDING_POSITION".equals(locTypeStr))
{
locType = LocationType.STATION;
if (!data.isNull("LogicalId"))
id = data.getInt("LogicalId");
else
id = data.getInt("id");
}
else
{
id = data.optInt("id", 0);
locType = LocationType.ADDRESS;
}
final Point coord;
final JSONObject posObj = data.optJSONObject("Position");
if (posObj != null)
{
final double lat = posObj.getDouble("Lat");
final double lon = posObj.getDouble("Long");
coord = new Point((int) Math.round(lat * 1E6), (int) Math.round(lon * 1E6));
}
else
{
coord = null;
}
final String name = data.getString("Name");
final String place = jsonOptString(data, "CityName");
return new Location(locType, id, coord.lat, coord.lon, place, name);
}
private Trip.Public parseJsonJourneyplannerPublicLeg(final JSONObject ptrInfo) throws JSONException
{
final DateFormat fullDateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.US);
final Line line = parseJsonLine(ptrInfo);
final JSONObject destObj = ptrInfo.optJSONObject("Direction");
String destinationName = jsonOptString(ptrInfo, "Destination");
if (destinationName == null && destObj != null)
destinationName = destObj.optString("Name");
final Location lineDestination = new Location(LocationType.ANY, 0, null, destinationName);
final Stop departureStop, arrivalStop;
final JSONObject departureInfo = ptrInfo.getJSONObject("Departure");
final JSONObject arrivalInfo = ptrInfo.getJSONObject("Arrival");
final Location departureLocation = parseJsonJourneyplannerLocation(departureInfo.getJSONObject("StopPlace"));
final Location arrivalLocation = parseJsonJourneyplannerLocation(arrivalInfo.getJSONObject("StopPlace"));
try
{
final Date departureTime = fullDateFormat.parse(departureInfo.getString("Time"));
final Date arrivalTime = fullDateFormat.parse(arrivalInfo.getString("Time"));
departureStop = new Stop(departureLocation, true, departureTime, null, null, null);
arrivalStop = new Stop(arrivalLocation, false, arrivalTime, null, null, null);
}
catch (final ParseException e)
{
throw new JSONException(e);
}
final JSONArray stepArray = ptrInfo.getJSONObject("steps").getJSONArray("Step");
List<Stop> intermediateStops = new ArrayList<Stop>(stepArray.length() - 1);
for (int i = 0; i < stepArray.length() - 1; i++)
{
final JSONObject enterStop = stepArray.getJSONObject(i).getJSONObject("Arrival");
final JSONObject leaveStop = stepArray.getJSONObject(i + 1).getJSONObject("Departure");
final Location location = parseJsonJourneyplannerLocation(leaveStop.getJSONObject("StopPlace"));
Date enterTime;
Date leaveTime;
try
{
enterTime = fullDateFormat.parse(enterStop.getString("Time"));
leaveTime = fullDateFormat.parse(leaveStop.getString("Time"));
intermediateStops.add(new Stop(location, enterTime, null, leaveTime, null));
}
catch (final ParseException e)
{
throw new JSONException(e);
}
}
final String message = jsonOptString(ptrInfo, "Notes");
return new Trip.Public(line, lineDestination, departureStop, arrivalStop, intermediateStops, null, message);
}
private Trip parseJsonJourneyplannerTrip(final JSONObject tripObject) throws JSONException
{
final JSONObject departureInfo = tripObject.getJSONObject("Departure");
final JSONObject arrivalInfo = tripObject.getJSONObject("Arrival");
final Location from = parseJsonJourneyplannerLocation(departureInfo.getJSONObject("Site"));
final Location to = parseJsonJourneyplannerLocation(arrivalInfo.getJSONObject("Site"));
final JSONArray legArray = tripObject.getJSONObject("sections").getJSONArray("Section");
final List<Trip.Leg> legs = new ArrayList<Trip.Leg>(legArray.length());
for (int i = 0; i < legArray.length(); i++)
{
legs.add(parseJsonJourneyplannerLeg(legArray.getJSONObject(i)));
}
return new Trip(null, from, to, legs, null, null, null);
}
private Line parseJsonLine(final JSONObject ptrInfo) throws JSONException
{
final JSONObject networkInfo = ptrInfo.optJSONObject("PTNetwork");
final String network;
if (networkInfo != null)
network = jsonOptString(networkInfo, "Name");
else
network = null;
final JSONObject lineInfo = ptrInfo.getJSONObject("Line");
final String transportMode = ptrInfo.getString("TransportMode");
final String lineNumber = lineInfo.optString("Number");
final String lineName = lineInfo.getString("Name");
final JSONObject operatorInfo = ptrInfo.optJSONObject("Operator");
final String operatorCode;
if (operatorInfo != null)
operatorCode = jsonOptString(operatorInfo, "Code");
else
operatorCode = null;
final String codeActivity = jsonOptString(ptrInfo, "CodeActivity");
final String lineLabel = createLineLabel(transportMode, lineNumber, lineName, operatorCode, codeActivity);
return new Line(lineInfo.getString("id"), lineLabel, lineStyle(network, lineLabel), null, null);
}
private Location parseJsonTransportLocation(final JSONObject data) throws JSONException
{
final int id = data.getInt("Id");
final LocationType locType;
switch (data.getInt("PointType"))
{
case 1:
locType = LocationType.POI;
break;
case 4:
locType = LocationType.STATION;
break;
case 3:
default:
locType = LocationType.ADDRESS;
}
final double lat = data.optDouble("Latitude", 0);
final double lon = data.optDouble("Longitude", 0);
final int latInt = (int) Math.round(lat * 1E6);
final int lonInt = (int) Math.round(lon * 1E6);
final String name = data.getString("Name");
String place = null;
final JSONObject localityObj = data.optJSONObject("Locality");
if (localityObj != null)
{
place = localityObj.getString("Name");
}
return new Location(locType, id, latInt, lonInt, place, name);
}
public QueryDeparturesResult queryDepartures(final int stationId, final int maxDepartures, final boolean equivs) throws IOException
{
throw new UnsupportedOperationException();
}
public QueryTripsResult queryMoreTrips(final QueryTripsContext context, final boolean later) throws IOException
{
return ((Context) context).queryMore(this, later);
}
public NearbyStationsResult queryNearbyStations(final Location location, final int maxDistance, final int maxStations) throws IOException
{
Location queryLocation = location;
if (!queryLocation.hasLocation())
{
if (location.type != LocationType.STATION)
throw new IllegalArgumentException("cannot handle: " + location.type);
queryLocation = jsonStationRequestCoord(location.id);
}
if (queryLocation == null)
throw new IllegalArgumentException("null location or station not found");
return jsonCoordRequest(location.lat, location.lon, maxDistance, maxStations);
}
public QueryTripsResult queryTrips(final Location from, final Location via, final Location to, final Date date, final boolean dep,
final Collection<Product> products, final WalkSpeed walkSpeed, final Accessibility accessibility, final Set<Option> options)
throws IOException
{
final List<Location> possibleFroms, possibleTos, possibleVias;
possibleFroms = identifyLocation(from);
possibleTos = identifyLocation(to);
if (via != null)
possibleVias = identifyLocation(via);
else
possibleVias = Collections.singletonList(null);
if (possibleFroms.isEmpty())
return new QueryTripsResult(HEADER, QueryTripsResult.Status.UNKNOWN_FROM);
if (possibleTos.isEmpty())
return new QueryTripsResult(HEADER, QueryTripsResult.Status.UNKNOWN_TO);
if (possibleVias.isEmpty())
return new QueryTripsResult(HEADER, QueryTripsResult.Status.UNKNOWN_VIA);
if (possibleFroms.size() > 1 || possibleVias.size() > 1 || possibleTos.size() > 1)
return new QueryTripsResult(HEADER, possibleFroms.size() > 1 ? possibleFroms : null, possibleVias.size() > 1 ? possibleVias : null,
possibleTos.size() > 1 ? possibleTos : null);
final Context context = new Context(possibleFroms.get(0), possibleVias.get(0), possibleTos.get(0), products, walkSpeed, accessibility,
options);
return queryTripsFromContext(context, date, dep);
}
protected QueryTripsResult queryTripsFromContext(final Context context, final Date date, final boolean dep) throws IOException
{
final String mode;
if (context.products != null)
{
final StringBuilder modeBuilder = new StringBuilder();
for (Product p : context.products)
{
final String productName = translateToLocalProduct(p);
if (productName != null)
modeBuilder.append(productName).append("|");
}
mode = modeBuilder.substring(0, modeBuilder.length() - 1);
}
else
{
mode = null;
}
final String walkSpeedStr = translateWalkSpeed(context.walkSpeed);
final StringBuilder parameters = buildCommonRequestParams("PlanTrip", "json");
parameters.append("&Disruptions=").append(0); // XXX what does this even mean?
parameters.append("&Algorithm=FASTEST");
parameters.append("&MaxWalkDist=1000"); // XXX good value? (in meters)
if (context.from.type == LocationType.STATION)
{
parameters.append("&DepType=STOP_PLACE&DepId=").append(context.from.id);
parameters.append("%240"); // "$0"
}
else if (context.from.type == LocationType.POI)
{
parameters.append("&DepType=POI&DepId=").append(context.from.id);
parameters.append("%240"); // "$0"
}
else
{
parameters.append("&DepLat=").append(latLonToDouble(context.from.lat));
parameters.append("&DepLon=").append(latLonToDouble(context.from.lon));
}
if (context.to.type == LocationType.STATION)
{
parameters.append("&ArrType=STOP_PLACE&ArrId=").append(context.to.id);
parameters.append("%240"); // "$0"
}
else if (context.to.type == LocationType.POI)
{
parameters.append("&ArrType=POI&ArrId=").append(context.to.id);
parameters.append("%240"); // "$0"
}
else
{
parameters.append("&ArrLat=").append(latLonToDouble(context.to.lat));
parameters.append("&ArrLon=").append(latLonToDouble(context.to.lon));
}
if (context.via != null)
{
if (context.via.type == LocationType.STATION)
{
parameters.append("&ViaType=STOP_PLACE&ViaId=").append(context.via.id);
parameters.append("%240"); // "$0"
}
else if (context.via.type == LocationType.POI)
{
parameters.append("&ViaType=POI&ViaId=").append(context.via.id);
parameters.append("%240"); // "$0"
}
else
{
parameters.append("&ViaLat=").append(latLonToDouble(context.via.lat));
parameters.append("&ViaLon=").append(latLonToDouble(context.via.lon));
}
}
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
parameters.append("&date=").append(dateFormat.format(date));
final DateFormat timeFormat = new SimpleDateFormat("HH-mm", Locale.US);
parameters.append(dep ? "&DepartureTime=" : "&ArrivalTime=").append(timeFormat.format(date));
parameters.append("&WalkSpeed=").append(walkSpeedStr);
if (mode != null)
parameters.append("&Modes=").append(ParserUtils.urlEncode(mode.toString(), UTF_8));
final StringBuilder uri = new StringBuilder(tripEndpoint);
uri.append(parameters);
final CharSequence page = ParserUtils.scrape(uri.toString(), null, UTF_8, null, 3);
try
{
final JSONObject head = new JSONObject(page.toString());
final JSONObject statusObj = head.optJSONObject("Status");
if (statusObj == null)
{
return new QueryTripsResult(HEADER, QueryTripsResult.Status.SERVICE_DOWN);
}
final String statusStr = statusObj.optString("Code");
if ("NO_SOLUTION_FOR_REQUEST".equals(statusStr))
{
return new QueryTripsResult(HEADER, QueryTripsResult.Status.NO_TRIPS);
}
if (!"OK".equals(statusStr))
{
return new QueryTripsResult(HEADER, QueryTripsResult.Status.SERVICE_DOWN);
}
final JSONArray tripArray = head.getJSONObject("trips").getJSONArray("Trip");
final List<Trip> trips = new ArrayList<Trip>(tripArray.length());
for (int i = 0; i < tripArray.length(); i++)
{
final JSONObject tripObject = tripArray.getJSONObject(i);
trips.add(parseJsonJourneyplannerTrip(tripObject));
}
if (trips.size() > 0)
{
context.updateEarliestArrival(trips.get(0).getLastArrivalTime());
context.updateLatestDeparture(trips.get(trips.size() - 1).getFirstDepartureTime());
}
return new QueryTripsResult(HEADER, uri.toString(), context.from, context.via, context.to, context, trips);
}
catch (final JSONException x)
{
throw new ParserException(x);
}
}
protected TimeZone timeZone()
{
return TimeZone.getTimeZone("Europe/Paris");
}
protected abstract String translateToLocalProduct(final Product p);
/**
* @param walkSpeed
* @return walk speed in km/h
*/
protected String translateWalkSpeed(final WalkSpeed walkSpeed)
{
switch (walkSpeed)
{
case FAST:
return "6";
case SLOW:
return "4";
case NORMAL:
default:
return "5";
}
}
}

View file

@ -34,6 +34,9 @@ public enum NetworkId
// Switzerland
SBB, BVB, VBL, ZVV,
// France
PACA,
// Belgium
SNCB,

View file

@ -0,0 +1,74 @@
/*
* Copyright 2014 Kjell Braden <afflux@pentabarf.de>
*
* 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
* 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 de.schildbach.pte.dto.Product;
/**
* @author Kjell Braden <afflux@pentabarf.de>
*/
public class PacaProvider extends AbstractTsiProvider
{
public static final NetworkId NETWORK_ID = NetworkId.PACA;
public PacaProvider()
{
super("PACA", "http://www.pacamobilite.fr/WebServices/TransinfoService/api");
}
public NetworkId id()
{
return NETWORK_ID;
}
public boolean hasCapabilities(final Capability... capabilities)
{
for (final Capability capability : capabilities)
if (capability == Capability.AUTOCOMPLETE_ONE_LINE || capability == Capability.TRIPS)
return true;
return false;
}
@Override
protected String translateToLocalProduct(final Product p)
{
switch (p)
{
case HIGH_SPEED_TRAIN:
return "RAPID_TRANSIT";
case REGIONAL_TRAIN:
return "TRAIN|LONG_DISTANCE_TRAIN";
case SUBURBAN_TRAIN:
return "LOCAL_TRAIN";
case SUBWAY:
return "METRO";
case TRAM:
return "TRAMWAY";
case BUS:
return "BUS|COACH";
case ON_DEMAND:
return "TOD";
case FERRY:
return "FERRY";
case CABLECAR:
default:
return null;
}
}
}

View file

@ -0,0 +1,180 @@
/*
* Copyright 2014 Kjell Braden <afflux@pentabarf.de>
*
* 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
* 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 java.util.List;
import org.junit.Test;
import de.schildbach.pte.NetworkProvider.Accessibility;
import de.schildbach.pte.NetworkProvider.WalkSpeed;
import de.schildbach.pte.PacaProvider;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.Product;
import de.schildbach.pte.dto.QueryTripsResult;
/**
* @author Kjell Braden <afflux@pentabarf.de>
*/
public class PacaProviderLiveTest extends AbstractProviderLiveTest
{
public PacaProviderLiveTest()
{
super(new PacaProvider());
}
@Test
public void autocomplete() throws Exception
{
final List<Location> autocompletes = provider.autocompleteStations("aeroport");
print(autocompletes);
}
@Test
public void autocompleteIdentified() throws Exception
{
final List<Location> autocompletes = provider.autocompleteStations("EGANAUDE, Biot");
print(autocompletes);
}
@Test
public void autocompleteUmlaut() throws Exception
{
final List<Location> autocompletes = provider.autocompleteStations("Aéroport");
print(autocompletes);
}
@Test
public void shortTrip() throws Exception
{
final QueryTripsResult result = queryTrips(new Location(LocationType.STATION, 61088, null, "Eganaude"), null, new Location(
LocationType.STATION, 58617, null, "Place de Gaulle"), new Date(), true, Product.ALL, WalkSpeed.NORMAL, Accessibility.NEUTRAL);
System.out.println(result);
assertEquals(QueryTripsResult.Status.OK, result.status);
assertTrue(result.trips.size() > 0);
if (!result.context.canQueryLater())
return;
final QueryTripsResult laterResult = queryMoreTrips(result.context, true);
System.out.println(laterResult);
if (!laterResult.context.canQueryLater())
return;
final QueryTripsResult later2Result = queryMoreTrips(laterResult.context, true);
System.out.println(later2Result);
if (!later2Result.context.canQueryLater())
return;
final QueryTripsResult later3Result = queryMoreTrips(later2Result.context, true);
System.out.println(later3Result);
if (!later3Result.context.canQueryLater())
return;
final QueryTripsResult later4Result = queryMoreTrips(later3Result.context, true);
System.out.println(later4Result);
if (!later4Result.context.canQueryLater())
return;
final QueryTripsResult later5Result = queryMoreTrips(later4Result.context, true);
System.out.println(later5Result);
if (!later5Result.context.canQueryLater())
return;
final QueryTripsResult later6Result = queryMoreTrips(later5Result.context, true);
System.out.println(later6Result);
if (!result.context.canQueryEarlier())
return;
final QueryTripsResult earlierResult = queryMoreTrips(result.context, false);
System.out.println(earlierResult);
if (!earlierResult.context.canQueryEarlier())
return;
final QueryTripsResult earlier2Result = queryMoreTrips(earlierResult.context, false);
System.out.println(earlier2Result);
if (!earlier2Result.context.canQueryEarlier())
return;
final QueryTripsResult earlier3Result = queryMoreTrips(earlier2Result.context, false);
System.out.println(earlier3Result);
if (!earlier3Result.context.canQueryEarlier())
return;
final QueryTripsResult earlier4Result = queryMoreTrips(earlier3Result.context, false);
System.out.println(earlier4Result);
}
@Test
public void slowTrip() throws Exception
{
final QueryTripsResult result = queryTrips(new Location(LocationType.STATION, 68629, 43441167, 5223055, "MARSEILLE", "Aeroport Hall 3 4"),
null, new Location(LocationType.STATION, 61088, 43623140, 7057545, "BIOT", "Eganaude"), new Date(), true, Product.ALL,
WalkSpeed.NORMAL, Accessibility.BARRIER_FREE);
System.out.println(result);
assertEquals(QueryTripsResult.Status.OK, result.status);
assertTrue(result.trips.size() > 0);
if (!result.context.canQueryLater())
return;
final QueryTripsResult laterResult = queryMoreTrips(result.context, true);
System.out.println(laterResult);
}
@Test
public void shortTripByName() throws Exception
{
final QueryTripsResult result = queryTrips(new Location(LocationType.ANY, 0, null, "Biot, Templiers!"), null, new Location(LocationType.ANY,
0, null, "Eganaude!"), new Date(), true, Product.ALL, WalkSpeed.NORMAL, Accessibility.NEUTRAL);
System.out.println(result);
}
@Test
public void slowTripPoi() throws Exception
{
final QueryTripsResult result = queryTrips(new Location(LocationType.POI, 30455, 0, 0, "CANNES", "Cannes"), null, new Location(
LocationType.POI, 30514, 0, 0, "NICE", "Nice"), new Date(), true, Product.ALL, WalkSpeed.NORMAL, Accessibility.NEUTRAL);
System.out.println(result);
assertEquals(QueryTripsResult.Status.OK, result.status);
assertTrue(result.trips.size() > 0);
if (!result.context.canQueryLater())
return;
final QueryTripsResult laterResult = queryMoreTrips(result.context, true);
System.out.println(laterResult);
}
}