/*
* Copyright 2014-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 .
*/
package de.schildbach.pte;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import javax.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import de.schildbach.pte.dto.Departure;
import de.schildbach.pte.dto.Line;
import de.schildbach.pte.dto.LineDestination;
import de.schildbach.pte.dto.Location;
import de.schildbach.pte.dto.LocationType;
import de.schildbach.pte.dto.NearbyLocationsResult;
import de.schildbach.pte.dto.NearbyLocationsResult.Status;
import de.schildbach.pte.dto.Point;
import de.schildbach.pte.dto.Position;
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.StationDepartures;
import de.schildbach.pte.dto.Stop;
import de.schildbach.pte.dto.Style;
import de.schildbach.pte.dto.Style.Shape;
import de.schildbach.pte.dto.SuggestLocationsResult;
import de.schildbach.pte.dto.SuggestedLocation;
import de.schildbach.pte.dto.Trip;
import de.schildbach.pte.dto.Trip.Individual;
import de.schildbach.pte.dto.Trip.Leg;
import de.schildbach.pte.dto.Trip.Public;
import de.schildbach.pte.exception.NotFoundException;
import de.schildbach.pte.exception.ParserException;
import de.schildbach.pte.util.ParserUtils;
import de.schildbach.pte.util.WordUtils;
/**
* @author Antonio El Khoury
* @author Andreas Schildbach
*/
public abstract class AbstractNavitiaProvider extends AbstractNetworkProvider
{
protected final static String SERVER_PRODUCT = "navitia";
protected final static String SERVER_VERSION = "v1";
protected static final String API_BASE = "http://api.navitia.io/" + SERVER_VERSION + "/";
private enum PlaceType
{
ADDRESS, ADMINISTRATIVE_REGION, POI, STOP_POINT, STOP_AREA
}
private enum SectionType
{
CROW_FLY, PUBLIC_TRANSPORT, STREET_NETWORK, TRANSFER, WAITING, STAY_IN, ON_DEMAND_TRANSPORT, BSS_RENT, BSS_PUT_BACK, BOARDING, LANDING
}
private enum TransferType
{
BIKE, WALKING
}
private enum PhysicalMode
{
AIR, BOAT, BUS, BUSRAPIDTRANSIT, COACH, FERRY, FUNICULAR, LOCALTRAIN, LONGDISTANCETRAIN, METRO, RAPIDTRANSIT, SHUTTLE, TAXI, TRAIN, TRAMWAY
}
@SuppressWarnings("serial")
private static class Context implements QueryTripsContext
{
private final Location from;
private final Location to;
private final String prevQueryUri;
private final String nextQueryUri;
private Context(final Location from, final Location to, final String prevQueryUri, final String nextQueryUri)
{
this.from = from;
this.to = to;
this.prevQueryUri = prevQueryUri;
this.nextQueryUri = nextQueryUri;
}
public boolean canQueryLater()
{
return (from != null && to != null && nextQueryUri != null);
}
public boolean canQueryEarlier()
{
return (from != null && to != null && prevQueryUri != null);
}
@Override
public String toString()
{
return getClass().getName() + "[" + from + "|" + to + "|" + prevQueryUri + "|" + nextQueryUri + "]";
}
}
private final String authorization;
public AbstractNavitiaProvider(final NetworkId network, final String authorization)
{
super(network);
this.authorization = authorization;
}
protected abstract String region();
protected int computeForegroundColor(final String lineColor)
{
int bgColor = Style.parseColor(lineColor);
return Style.deriveForegroundColor(bgColor);
}
protected Style getLineStyle(final Product product, final String code, final String color)
{
return new Style(Shape.RECT, Style.parseColor(color), computeForegroundColor(color));
}
private String uri()
{
return API_BASE + "coverage/" + region() + "/";
}
private String tripUri()
{
return API_BASE;
}
private Point parseCoord(final JSONObject coord) throws IOException
{
try
{
final double lat = coord.getDouble("lat");
final double lon = coord.getDouble("lon");
return Point.fromDouble(lat, lon);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private Location parseStopPoint(final JSONObject stopPoint) throws IOException
{
try
{
final String id = stopPoint.getString("id");
final JSONObject coord = stopPoint.getJSONObject("coord");
final Point point = parseCoord(coord);
final String name = WordUtils.capitalizeFully(stopPoint.getString("name"));
return new Location(LocationType.STATION, id, point, null, name);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private Location parseLocation(final JSONObject place) throws IOException
{
try
{
final String type = place.getString("embedded_type");
final PlaceType placeType = PlaceType.valueOf(type.toUpperCase());
switch (placeType)
{
case STOP_POINT:
{
final JSONObject stopPoint = place.getJSONObject("stop_point");
return parseStopPoint(stopPoint);
}
case STOP_AREA:
{
final JSONObject stopArea = place.getJSONObject("stop_area");
return parseStopPoint(stopArea);
}
case ADDRESS:
{
final JSONObject address = place.getJSONObject("address");
final String id = address.getString("id");
final JSONObject coord = address.getJSONObject("coord");
final Point point = parseCoord(coord);
final String name = WordUtils.capitalizeFully(place.getString("name"));
return new Location(LocationType.ADDRESS, id, point, null, name);
}
case POI:
{
final JSONObject poi = place.getJSONObject("poi");
final String id = poi.getString("id");
final JSONObject coord = poi.getJSONObject("coord");
final Point point = parseCoord(coord);
final String name = WordUtils.capitalizeFully(poi.getString("name"));
return new Location(LocationType.ADDRESS, id, point.lat, point.lon, null, name);
}
default:
throw new IllegalArgumentException("Unhandled place type: " + type);
}
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private String printLocation(final Location location)
{
if (location.hasId())
return location.id;
else if (location.hasLocation())
return (double) (location.lon) / 1E6 + ";" + (double) (location.lat) / 1E6;
else
return "";
}
private Date parseDate(final String dateString) throws ParseException
{
return new SimpleDateFormat("yyyyMMdd'T'HHmmss").parse(dateString);
}
private String printDate(final Date date)
{
return new SimpleDateFormat("yyyyMMdd'T'HHmmss").format(date);
}
private LinkedList parsePath(final JSONArray coordinates) throws IOException
{
LinkedList path = new LinkedList();
for (int i = 0; i < coordinates.length(); ++i)
{
try
{
final JSONArray jsonPoint = coordinates.getJSONArray(i);
final double lon = jsonPoint.getDouble(0);
final double lat = jsonPoint.getDouble(1);
final Point point = Point.fromDouble(lat, lon);
path.add(point);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
return path;
}
private class LegInfo
{
public final Location departure;
public final Date departureTime;
public final Location arrival;
public final Date arrivalTime;
public final List path;
public final int distance;
public final int min;
public LegInfo(final Location departure, final Date departureTime, final Location arrival, final Date arrivalTime, final List path,
final int distance, final int min)
{
this.departure = departure;
this.departureTime = departureTime;
this.arrival = arrival;
this.arrivalTime = arrivalTime;
this.path = path;
this.distance = distance;
this.min = min;
}
}
private LegInfo parseLegInfo(final JSONObject section) throws IOException
{
try
{
final String type = section.getString("type");
if (!type.equals("waiting"))
{
// Build departure location.
final JSONObject sectionFrom = section.getJSONObject("from");
final Location departure = parseLocation(sectionFrom);
// Build departure time.
final String departureDateTime = section.getString("departure_date_time");
final Date departureTime = parseDate(departureDateTime);
// Build arrival location.
final JSONObject sectionTo = section.getJSONObject("to");
final Location arrival = parseLocation(sectionTo);
// Build arrival time.
final String arrivalDateTime = section.getString("arrival_date_time");
final Date arrivalTime = parseDate(arrivalDateTime);
// Build path and distance. Check first that geojson
// object exists.
LinkedList path = null;
int distance = 0;
if (section.has("geojson"))
{
final JSONObject jsonPath = section.getJSONObject("geojson");
final JSONArray coordinates = jsonPath.getJSONArray("coordinates");
path = parsePath(coordinates);
final JSONArray properties = jsonPath.getJSONArray("properties");
for (int i = 0; i < properties.length(); ++i)
{
final JSONObject property = properties.getJSONObject(i);
if (property.has("length"))
{
distance = property.getInt("length");
break;
}
}
}
// Build duration in min.
final int min = section.getInt("duration") / 60;
return new LegInfo(departure, departureTime, arrival, arrivalTime, path, distance, min);
}
else
{
return null;
}
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
catch (final ParseException parseExc)
{
throw new ParserException(parseExc);
}
}
private Line parseLineFromSection(final JSONObject section) throws IOException
{
try
{
final JSONArray links = section.getJSONArray("links");
String lineId = null;
String modeId = null;
for (int i = 0; i < links.length(); ++i)
{
final JSONObject link = links.getJSONObject(i);
final String linkType = link.getString("type");
if (linkType.equals("line"))
lineId = link.getString("id");
else if (linkType.equals("physical_mode"))
modeId = link.getString("id");
}
final Product product = parseLineProductFromMode(modeId);
final JSONObject displayInfo = section.getJSONObject("display_informations");
final String code = displayInfo.getString("code");
final String colorHex = displayInfo.getString("color");
final String color = colorHex.equals("000000") ? "#FFFFFF" : "#" + colorHex;
final Style lineStyle = getLineStyle(product, code, color);
return new Line(lineId, null, product, code, lineStyle);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private Stop parseStop(final JSONObject stopDateTime) throws IOException
{
try
{
// Build location.
final JSONObject stopPoint = stopDateTime.getJSONObject("stop_point");
final Location location = parseStopPoint(stopPoint);
// Build planned arrival time.
final Date plannedArrivalTime = parseDate(stopDateTime.getString("arrival_date_time"));
// Build planned arrival position.
final Position plannedArrivalPosition = null;
// Build planned departure time.
final Date plannedDepartureTime = parseDate(stopDateTime.getString("departure_date_time"));
// Build planned departure position.
final Position plannedDeparturePosition = null;
return new Stop(location, plannedArrivalTime, plannedArrivalPosition, plannedDepartureTime, plannedDeparturePosition);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
catch (final ParseException parseExc)
{
throw new ParserException(parseExc);
}
}
private Leg parseLeg(final JSONObject section) throws IOException
{
try
{
// Build common leg info.
final LegInfo legInfo = parseLegInfo(section);
if (legInfo == null)
return null;
final String type = section.getString("type");
final SectionType sectionType = SectionType.valueOf(type.toUpperCase());
switch (sectionType)
{
case CROW_FLY:
{
// Return null leg if duration is 0.
if (legInfo.min == 0)
return null;
// Build type.
final Individual.Type individualType = Individual.Type.WALK;
return new Individual(individualType, legInfo.departure, legInfo.departureTime, legInfo.arrival, legInfo.arrivalTime,
legInfo.path, legInfo.distance);
}
case PUBLIC_TRANSPORT:
{
// Build line.
final Line line = parseLineFromSection(section);
// Build destination.
final JSONObject displayInfo = section.getJSONObject("display_informations");
final String direction = displayInfo.getString("direction");
final Location destination = new Location(LocationType.STATION, direction, direction, direction);
final JSONArray stopDateTimes = section.getJSONArray("stop_date_times");
final int nbStopDateTime = stopDateTimes.length();
// Build departure stop.
final Stop departureStop = parseStop(stopDateTimes.getJSONObject(0));
// Build arrival stop.
final Stop arrivalStop = parseStop(stopDateTimes.getJSONObject(nbStopDateTime - 1));
// Build intermediate stops.
final LinkedList intermediateStops = new LinkedList();
for (int i = 1; i < nbStopDateTime - 1; ++i)
{
final Stop intermediateStop = parseStop(stopDateTimes.getJSONObject(i));
intermediateStops.add(intermediateStop);
}
// Build message.
final String message = null;
return new Public(line, destination, departureStop, arrivalStop, intermediateStops, legInfo.path, message);
}
case STREET_NETWORK:
{
final String modeType = section.getString("mode");
final TransferType transferType = TransferType.valueOf(modeType.toUpperCase());
// Build type.
final Individual.Type individualType;
switch (transferType)
{
case BIKE:
individualType = Individual.Type.BIKE;
break;
case WALKING:
individualType = Individual.Type.WALK;
break;
default:
throw new IllegalArgumentException("Unhandled transfer type: " + modeType);
}
return new Individual(individualType, legInfo.departure, legInfo.departureTime, legInfo.arrival, legInfo.arrivalTime,
legInfo.path, legInfo.distance);
}
case TRANSFER:
{
// Build type.
final Individual.Type individualType = Individual.Type.WALK;
return new Individual(individualType, legInfo.departure, legInfo.departureTime, legInfo.arrival, legInfo.arrivalTime,
legInfo.path, legInfo.distance);
}
case WAITING:
{
return null;
// Do not add leg in case of waiting on the peer.
}
default:
throw new IllegalArgumentException("Unhandled place type: " + type);
}
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private void parseQueryTripsResult(final JSONObject head, final Location from, final Location to, final QueryTripsResult result)
throws IOException
{
try
{
// Fill trips.
final JSONArray journeys = head.getJSONArray("journeys");
for (int i = 0; i < journeys.length(); ++i)
{
final JSONObject journey = journeys.getJSONObject(i);
final int changeCount = journey.getInt("nb_transfers");
// Build leg list.
final List legs = new LinkedList();
final JSONArray sections = journey.getJSONArray("sections");
for (int j = 0; j < sections.length(); ++j)
{
final JSONObject section = sections.getJSONObject(j);
final Leg leg = parseLeg(section);
if (leg != null)
legs.add(leg);
}
result.trips.add(new Trip(null, from, to, legs, null, null, changeCount));
}
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private Line parseLine(final JSONObject jsonLine) throws IOException
{
try
{
final String lineId = jsonLine.getString("id");
final Product product = parseLineProduct(jsonLine);
final String code = jsonLine.getString("code");
final String color = "#" + jsonLine.getString("color");
final Style lineStyle = getLineStyle(product, code, color);
return new Line(lineId, null, product, code, lineStyle);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private Map lineProductCache = new WeakHashMap();
private Product parseLineProductFromMode(final String modeId)
{
final String modeType = modeId.replace("physical_mode:", "");
final PhysicalMode physicalMode = PhysicalMode.valueOf(modeType.toUpperCase());
switch (physicalMode)
{
case BUS:
case BUSRAPIDTRANSIT:
case COACH:
case SHUTTLE:
return Product.BUS;
case RAPIDTRANSIT:
case TRAIN:
case LOCALTRAIN:
case LONGDISTANCETRAIN:
return Product.SUBURBAN_TRAIN;
case TRAMWAY:
return Product.TRAM;
case METRO:
return Product.SUBWAY;
case FERRY:
return Product.FERRY;
case FUNICULAR:
return Product.CABLECAR;
case TAXI:
return Product.ON_DEMAND;
default:
throw new IllegalArgumentException("Unhandled place type: " + modeId);
}
}
private Product parseLineProduct(final JSONObject line) throws IOException
{
try
{
final String lineId = line.getString("id");
final Product cachedProduct = lineProductCache.get(lineId);
if (cachedProduct != null)
return cachedProduct;
final JSONObject mode = getLinePhysicalMode(lineId);
final String modeId = mode.getString("id");
final Product product = parseLineProductFromMode(modeId);
lineProductCache.put(lineId, product);
return product;
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private JSONObject getLinePhysicalMode(final String lineId) throws IOException
{
final String uri = uri() + "lines/" + ParserUtils.urlEncode(lineId) + "/physical_modes";
final CharSequence page = ParserUtils.scrape(uri, authorization);
try
{
final JSONObject head = new JSONObject(page.toString());
final JSONArray physicalModes = head.getJSONArray("physical_modes");
final JSONObject physicalMode = physicalModes.getJSONObject(0);
return physicalMode;
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private LineDestination parseLineDestination(final JSONObject route) throws IOException
{
try
{
// Build line.
final JSONObject jsonLine = route.getJSONObject("line");
final Line line = parseLine(jsonLine);
// Build line destination.
final JSONObject direction = route.getJSONObject("direction");
Location destination = parseLocation(direction);
return new LineDestination(line, destination);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private List getStationLines(final String stopPointId) throws IOException
{
final String uri = uri() + "stop_points/" + ParserUtils.urlEncode(stopPointId) + "/routes?depth=2";
final CharSequence page = ParserUtils.scrape(uri, authorization);
try
{
final JSONObject head = new JSONObject(page.toString());
final JSONArray routes = head.getJSONArray("routes");
final List lineDestinations = new LinkedList();
for (int i = 0; i < routes.length(); ++i)
{
final JSONObject route = routes.getJSONObject(i);
final LineDestination lineDestination = parseLineDestination(route);
lineDestinations.add(lineDestination);
}
return lineDestinations;
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
private String getStopAreaId(final String stopPointId) throws IOException
{
final String uri = uri() + "stop_points/" + ParserUtils.urlEncode(stopPointId) + "?depth=1";
final CharSequence page = ParserUtils.scrape(uri, authorization);
try
{
final JSONObject head = new JSONObject(page.toString());
final JSONArray stopPoints = head.getJSONArray("stop_points");
final JSONObject stopPoint = stopPoints.getJSONObject(0);
final JSONObject stopArea = stopPoint.getJSONObject("stop_area");
return stopArea.getString("id");
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
@Override
protected boolean hasCapability(final Capability capability)
{
if (capability == Capability.SUGGEST_LOCATIONS || capability == Capability.NEARBY_LOCATIONS || capability == Capability.DEPARTURES
|| capability == Capability.TRIPS)
return true;
else
return false;
}
public NearbyLocationsResult queryNearbyLocations(final EnumSet types, final Location location, final int maxDistance,
final int maxLocations) throws IOException
{
final ResultHeader resultHeader = new ResultHeader(network, SERVER_PRODUCT, SERVER_VERSION, 0, null);
// Build query uri depending of location type.
final String queryUriType;
if (location.type == LocationType.COORD || location.type == LocationType.ADDRESS || location.type == LocationType.ANY)
{
if (!location.hasLocation())
{
throw new IllegalArgumentException();
}
final double lon = location.lon / 1E6;
final double lat = location.lat / 1E6;
queryUriType = "coords/" + lon + ";" + lat + "/";
}
else if (location.type == LocationType.STATION)
{
if (!location.isIdentified())
{
throw new IllegalArgumentException();
}
queryUriType = "stop_point/" + location.id + "/";
}
else if (location.type == LocationType.POI)
{
if (!location.isIdentified())
{
throw new IllegalArgumentException();
}
queryUriType = "poi/" + location.id + "/";
}
else
{
throw new IllegalArgumentException("Unhandled location type: " + location.type);
}
final String queryUri = uri() + queryUriType + "places_nearby?type[]=stop_point" + "&distance=" + maxDistance + "&count=" + maxLocations
+ "&depth=0";
final CharSequence page = ParserUtils.scrape(queryUri, authorization);
// System.out.println(queryUri);
try
{
final JSONObject head = new JSONObject(page.toString());
final JSONObject pagination = head.getJSONObject("pagination");
final int nbResults = pagination.getInt("total_result");
// If no result is available, location id must be
// faulty.
if (nbResults == 0)
{
return new NearbyLocationsResult(resultHeader, Status.INVALID_ID);
}
else
{
final List stations = new ArrayList();
final JSONArray places = head.getJSONArray("places_nearby");
// Cycle through nearby stations.
for (int i = 0; i < places.length(); ++i)
{
final JSONObject place = places.getJSONObject(i);
// Add location to station list only if
// station is active, i.e. at least one
// departure exists within one hour.
final Location nearbyLocation = parseLocation(place);
stations.add(nearbyLocation);
}
return new NearbyLocationsResult(resultHeader, stations);
}
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
public QueryDeparturesResult queryDepartures(final String stationId, final @Nullable Date time, final int maxDepartures, final boolean equivs)
throws IOException
{
final ResultHeader resultHeader = new ResultHeader(network, SERVER_PRODUCT, SERVER_VERSION, 0, null);
try
{
final QueryDeparturesResult result = new QueryDeparturesResult(resultHeader, QueryDeparturesResult.Status.OK);
final String dateTime = printDate(time);
// If equivs is equal to true, get stop_area corresponding
// to stop_point and query departures.
final StringBuilder queryUri = new StringBuilder();
queryUri.append(uri());
if (equivs)
{
final String header = stationId.substring(0, stationId.indexOf(":"));
if (header.equals("stop_point"))
{
final String stopAreaId = getStopAreaId(stationId);
queryUri.append("stop_areas/" + stopAreaId + "/");
}
else if (header.equals("stop_area"))
{
queryUri.append("stop_areas/" + stationId + "/");
}
}
else
{
queryUri.append("stop_points/" + stationId + "/");
}
queryUri.append("departures?from_datetime=" + dateTime + "&count=" + maxDepartures + "&duration=3600" + "&depth=0");
final CharSequence page = ParserUtils.scrape(queryUri.toString(), authorization);
// System.out.println(queryUri);
final JSONObject head = new JSONObject(page.toString());
final JSONArray departures = head.getJSONArray("departures");
// Fill departures in StationDepartures.
for (int i = 0; i < departures.length(); ++i)
{
final JSONObject jsonDeparture = departures.getJSONObject(i);
final JSONObject stopPoint = jsonDeparture.getJSONObject("stop_point");
final Location location = parseStopPoint(stopPoint);
// If stop point has already been added, retrieve it
// from result, otherwise add it and add station
// lines.
StationDepartures stationDepartures = result.findStationDepartures(location.id);
if (stationDepartures == null)
{
stationDepartures = new StationDepartures(location, new LinkedList(), new LinkedList());
result.stationDepartures.add(stationDepartures);
final List lineDestinations = getStationLines(location.id);
for (LineDestination lineDestination : lineDestinations)
checkNotNull(stationDepartures.lines).add(lineDestination);
}
// Build departure date.
final JSONObject stopDateTime = jsonDeparture.getJSONObject("stop_date_time");
final String departureDateTime = stopDateTime.getString("departure_date_time");
final Date plannedTime = parseDate(departureDateTime);
// Build line.
final JSONObject route = jsonDeparture.getJSONObject("route");
final JSONObject jsonLine = route.getJSONObject("line");
final Line line = parseLine(jsonLine);
final Location destination = findLineDestination(stationDepartures.lines, line).destination;
// Add departure to list.
final Departure departure = new Departure(plannedTime, null, line, null, destination, null, null);
stationDepartures.departures.add(departure);
}
return result;
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
catch (final ParseException parseExc)
{
throw new ParserException(parseExc);
}
catch (final NotFoundException fnfExc)
{
try
{
final JSONObject head = new JSONObject(fnfExc.scrapeErrorStream().toString());
final JSONObject error = head.getJSONObject("error");
final String id = error.getString("id");
if (id.equals("unknown_object"))
return new QueryDeparturesResult(resultHeader, QueryDeparturesResult.Status.INVALID_STATION);
else
throw new IllegalArgumentException("Unhandled error id: " + id);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
}
private LineDestination findLineDestination(final List lineDestinations, final Line line)
{
for (final LineDestination lineDestination : lineDestinations)
if (lineDestination.line.equals(line))
return lineDestination;
return null;
}
public SuggestLocationsResult suggestLocations(final CharSequence constraint) throws IOException
{
final String nameCstr = constraint.toString();
final String queryUri = uri() + "places?q=" + ParserUtils.urlEncode(nameCstr) + "&type[]=stop_area&type[]=address" + "&depth=1";
final CharSequence page = ParserUtils.scrape(queryUri, authorization);
// System.out.println(queryUri);
try
{
final List locations = new ArrayList();
final JSONObject head = new JSONObject(page.toString());
if (head.has("places"))
{
final JSONArray places = head.getJSONArray("places");
for (int i = 0; i < places.length(); ++i)
{
final JSONObject place = places.getJSONObject(i);
// Add location to station list.
final Location location = parseLocation(place);
locations.add(new SuggestedLocation(location));
}
}
final ResultHeader resultHeader = new ResultHeader(network, SERVER_PRODUCT, SERVER_VERSION, 0, null);
return new SuggestLocationsResult(resultHeader, locations);
}
catch (final JSONException jsonExc)
{
throw new ParserException(jsonExc);
}
}
public QueryTripsResult queryTrips(final Location from, final @Nullable Location via, final Location to, final Date date, final boolean dep,
final @Nullable Set products, final @Nullable WalkSpeed walkSpeed, final @Nullable Accessibility accessibility,
final @Nullable Set