/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser 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 org.opentripplanner.graph_builder.impl.raptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.TimeZone;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.gtfs.model.Stop;
import org.onebusaway.gtfs.model.Trip;
import org.onebusaway.gtfs.services.calendar.CalendarService;
import org.opentripplanner.common.geometry.DistanceLibrary;
import org.opentripplanner.common.geometry.SphericalDistanceLibrary;
import org.opentripplanner.common.model.T2;
import org.opentripplanner.common.pqueue.BinHeap;
import org.opentripplanner.graph_builder.services.GraphBuilder;
import org.opentripplanner.routing.algorithm.GenericDijkstra;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.ServiceDay;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.edgetype.InterlineDwellData;
import org.opentripplanner.routing.edgetype.PatternHop;
import org.opentripplanner.routing.edgetype.PatternInterlineDwell;
import org.opentripplanner.routing.edgetype.PreAlightEdge;
import org.opentripplanner.routing.edgetype.PreBoardEdge;
import org.opentripplanner.routing.edgetype.TransitBoardAlight;
import org.opentripplanner.routing.graph.AbstractVertex;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.impl.StreetVertexIndexServiceImpl;
import org.opentripplanner.routing.impl.raptor.MaxTransitRegions;
import org.opentripplanner.routing.impl.raptor.MaxWalkState;
import org.opentripplanner.routing.impl.raptor.Raptor;
import org.opentripplanner.routing.impl.raptor.RaptorData;
import org.opentripplanner.routing.impl.raptor.RaptorDataService;
import org.opentripplanner.routing.impl.raptor.RaptorInterlineData;
import org.opentripplanner.routing.impl.raptor.RaptorRoute;
import org.opentripplanner.routing.impl.raptor.RaptorState;
import org.opentripplanner.routing.impl.raptor.RaptorStateSet;
import org.opentripplanner.routing.impl.raptor.RaptorStop;
import org.opentripplanner.routing.impl.raptor.RegionData;
import org.opentripplanner.routing.impl.raptor.RouteSegmentComparator;
import org.opentripplanner.routing.services.TransitIndexService;
import org.opentripplanner.routing.spt.ShortestPathTree;
import org.opentripplanner.routing.transit_index.RouteSegment;
import org.opentripplanner.routing.transit_index.RouteVariant;
import org.opentripplanner.routing.vertextype.OnboardVertex;
import org.opentripplanner.routing.vertextype.TransitStop;
import org.opentripplanner.routing.vertextype.TransitStopArrive;
import org.opentripplanner.routing.vertextype.TransitStopDepart;
import org.opentripplanner.util.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vividsolutions.jts.geom.Coordinate;
public class RaptorDataBuilder implements GraphBuilder {
private static final Logger log = LoggerFactory.getLogger(RaptorDataBuilder.class);
private static final double MIN_SPEED = 1.33;
private static final double MAX_DISTANCE = 3218;
private static final int N_REGIONS = 100;
private DistanceLibrary distanceLibrary = SphericalDistanceLibrary.getInstance();
private int MAX_TRANSFERS = 7;
@SuppressWarnings("unchecked")
public void buildGraph(Graph graph, HashMap<Class<?>, Object> extra) {
RaptorData data = new RaptorData();
TransitIndexService transitIndex = graph.getService(TransitIndexService.class);
int nTotalStops = 0;
for (Vertex v : graph.getVertices()) {
if (v instanceof TransitStop) {
nTotalStops++;
}
}
data.routesForStop = new List[nTotalStops];
data.stops = new RaptorStop[nTotalStops];
HashMap<AgencyAndId, RaptorRoute> raptorRouteForTrip = new HashMap<AgencyAndId, RaptorRoute>();
ArrayList<PatternInterlineDwell> interlines = new ArrayList<PatternInterlineDwell>();
for (String agency : transitIndex.getAllAgencies()) {
for (RouteVariant variant : transitIndex.getVariantsForAgency(agency)) {
List<Stop> variantStops = variant.getStops();
final int nStops = variantStops.size();
int nPatterns = variant.getSegments().size() / nStops;
RaptorRoute route = new RaptorRoute(nStops, nPatterns);
route.mode = ((PatternHop)variant.getSegments().get(0).hopOut).getMode();
data.routes.add(route);
interlines.addAll(variant.getInterlines());
for (int i = 0; i < nStops; ++i) {
final Stop stop = variantStops.get(i);
RaptorStop raptorStop = makeRaptorStop(data, stop);
route.stops[i] = raptorStop;
if (data.routesForStop[raptorStop.index] == null)
data.routesForStop[raptorStop.index] = new ArrayList<RaptorRoute>();
data.routesForStop[raptorStop.index].add(route);
}
List<RouteSegment> segments = variant.getSegments();
// this sorter ensures that route segments are ordered by stop sequence, and, at a
// given stop, patterns are in a consistent order
Collections.sort(segments, new RouteSegmentComparator());
int stop = 0;
int pattern = 0;
for (RouteSegment segment : segments) {
if (stop == 0) {
for (Trip trip : ((TransitBoardAlight)segment.board).getPattern().getTrips()) {
raptorRouteForTrip.put(trip.getId(), route);
}
}
if (stop != nStops - 1) {
for (Edge e : segment.board.getFromVertex().getIncoming()) {
if (e instanceof PreBoardEdge) {
route.stops[stop].stopVertex = (TransitStop) e.getFromVertex();
route.stops[stop].departVertex = (TransitStopDepart) e.getToVertex();
}
}
route.boards[stop][pattern] = (TransitBoardAlight) segment.board;
}
if (stop != 0) {
for (Edge e : segment.alight.getToVertex().getOutgoing()) {
if (e instanceof PreAlightEdge) {
route.stops[stop].stopVertex = (TransitStop) e.getToVertex();
route.stops[stop].arriveVertex = (TransitStopArrive) e.getFromVertex();
}
}
route.alights[stop - 1][pattern] = (TransitBoardAlight) segment.alight;
}
if (++pattern == nPatterns) {
pattern = 0;
stop++;
}
}
if (stop != nStops || pattern != 0) {
throw new RuntimeException("Wrong number of segments");
}
}
}
for (PatternInterlineDwell interline : interlines) {
for (Map.Entry<AgencyAndId, InterlineDwellData> entry : interline
.getTripIdToInterlineDwellData().entrySet()) {
InterlineDwellData dwellData = entry.getValue();
AgencyAndId fromTripId = entry.getKey();
AgencyAndId toTripId = dwellData.trip.getId();
RaptorInterlineData interlineData = new RaptorInterlineData();
interlineData.fromTripId = fromTripId;
interlineData.toTripId = toTripId;
interlineData.fromRoute = raptorRouteForTrip.get(fromTripId);
interlineData.toRoute = raptorRouteForTrip.get(toTripId);
// figure out which alight this is attached to
final int fromNStops = interlineData.fromRoute.getNStops();
for (int i = 0; i < interlineData.fromRoute.alights[0].length; ++i) {
TransitBoardAlight alight = interlineData.fromRoute.alights[fromNStops - 2][i];
if (alight.getFromVertex() == interline.getFromVertex()) {
// found pattern
interlineData.fromPatternIndex = i;
// need to find trip
List<Trip> trips = alight.getPattern().getTrips();
for (int tripIndex = 0; tripIndex < trips.size(); ++tripIndex) {
Trip trip = trips.get(tripIndex);
if (trip.getId().equals(fromTripId)) {
interlineData.fromTripIndex = tripIndex;
break;
}
}
break;
}
}
// and which board
for (int i = 0; i < interlineData.toRoute.boards[0].length; ++i) {
TransitBoardAlight board = interlineData.toRoute.boards[0][i];
if (board.getToVertex() == interline.getToVertex()) {
// found pattern
interlineData.toPatternIndex = i;
// need to find trip
List<Trip> trips = board.getPattern().getTrips();
for (int tripIndex = 0; tripIndex < trips.size(); ++tripIndex) {
Trip trip = trips.get(tripIndex);
if (trip.getId().equals(toTripId)) {
interlineData.toTripIndex = tripIndex;
break;
}
}
break;
}
}
interlineData.fromRoute.interlinesOut.put(fromTripId, interlineData);
interlineData.toRoute.interlinesIn.put(toTripId, interlineData);
}
}
data.stops = Arrays.copyOfRange(data.stops, 0, data.raptorStopsForStopId.size());
nTotalStops = data.stops.length;
// initNearbyStops();
graph.putService(RaptorDataService.class, new RaptorDataService(data));
//MaxTransitRegions regions = makeMaxTransitRegions(graph, data);
//data.maxTransitRegions = regions;
data.regionData = makeRegionsBySubdivision(graph, data);
}
private MaxTransitRegions makeMaxTransitRegions(Graph graph, RaptorData data) {
ArrayList<Vertex> vertices = new ArrayList<Vertex>();
for (Vertex v : graph.getVertices()) {
if (v instanceof TransitStop) {
vertices.add(v);
}
}
ArrayList<ArrayList<Vertex>> verticesForRegion = new ArrayList<ArrayList<Vertex>>();
int nRegions = split(verticesForRegion, null, vertices, 0, true, vertices.size() / 20);
for (int region = 0; region < verticesForRegion.size(); ++region) {
for (Vertex vertex : verticesForRegion.get(region)) {
vertex.setGroupIndex(region);
}
}
MaxTransitRegions regions = new MaxTransitRegions();
//mapping of stops to routes served
HashMap<Vertex, List<RaptorRoute>> routesForVertex = new HashMap<Vertex, List<RaptorRoute>>();
for (RaptorRoute route : data.routes) {
for (RaptorStop stop : route.stops) {
MapUtils.addToMapList(routesForVertex, stop.stopVertex, route);
}
}
// compute stop-to-stop walk times
HashMap<Vertex, T2<Integer, Double>>[] stopToStopWalkTimes = computeStopToStopWalkTimes(
vertices, MIN_SPEED, MAX_DISTANCE, routesForVertex );
regions.minSpeed = MIN_SPEED;
regions.maxDistance = MAX_DISTANCE;
final int NDAYS = 5;
CalendarService calendarService = graph.getCalendarService();
TimeZone timeZone = graph.getTimeZone();
// get start of today
Calendar calendar = Calendar.getInstance(timeZone);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
regions.startYear = year;
regions.startMonth = month;
regions.startDay = day;
regions.maxTransit = new int[NDAYS][nRegions][nRegions];
// todo slack
Map<List<ServiceDay>, int[][]> cache = new HashMap<List<ServiceDay>, int[][]>();
for (int d = 0; d < NDAYS; ++d) {
calendar = Calendar.getInstance(timeZone);
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.add(Calendar.DAY_OF_MONTH, -1);
int yesterday = (int) (calendar.getTime().getTime() / 1000);
calendar.add(Calendar.DAY_OF_MONTH, 1);
int today = (int) (calendar.getTime().getTime() / 1000);
calendar.add(Calendar.DAY_OF_MONTH, 1);
int tomorrow = (int) (calendar.getTime().getTime() / 1000);
ArrayList<ServiceDay> serviceDays = new ArrayList<ServiceDay>();
for (String agency : graph.getAgencyIds()) {
serviceDays.add(new ServiceDay(graph, yesterday, calendarService, agency));
serviceDays.add(new ServiceDay(graph, today, calendarService, agency));
serviceDays.add(new ServiceDay(graph, tomorrow, calendarService, agency));
}
int[][] cached = cache.get(serviceDays);
if (cached != null) {
log.debug("using cached values for " + calendar);
regions.maxTransit[d] = cached;
continue;
}
log.debug("Computing max transit data for day " + d);
for (int region = 0; region < nRegions; ++region) {
log.debug("Computing max transit data for region " + region);
regions.maxTransit[d][region] = computeMaxTransitData(data, serviceDays,
verticesForRegion.get(region), stopToStopWalkTimes, today, nRegions, region);
}
}
return regions;
}
private HashMap<Vertex, T2<Integer, Double>>[] computeStopToStopWalkTimes(
ArrayList<Vertex> vertices, double minSpeed, double maxDistance,
HashMap<Vertex, List<RaptorRoute>> routesForVertex) {
log.debug("Finding stop-to-stop walk times");
@SuppressWarnings("unchecked")
HashMap<Vertex, T2<Integer, Double>>[] times = new HashMap[AbstractVertex.getMaxIndex()];
RoutingRequest walkOptions = new RoutingRequest(TraverseMode.WALK);
walkOptions.setWalkSpeed(minSpeed);
walkOptions.setArriveBy(true);
walkOptions.setMaxWalkDistance(maxDistance);
GenericDijkstra dijkstra = new GenericDijkstra(walkOptions);
for (Vertex destination : vertices) {
List<RaptorRoute> destinationRoutes = routesForVertex.get(destination);
final HashMap<Vertex, T2<Integer, Double>> timesByDestination = new HashMap<Vertex, T2<Integer, Double>>();
times[destination.getIndex()] = timesByDestination;
State initialState = new MaxWalkState(destination, walkOptions);
ShortestPathTree spt = dijkstra.getShortestPathTree(initialState);
for (State state : spt.getAllStates()) {
Vertex vertex = state.getVertex();
if (vertex instanceof TransitStop) {
final List<RaptorRoute> vertexRoutes = routesForVertex.get(vertex);
if (vertexRoutes == null) {
//this stop is not visited by any routes.
continue;
}
if (isSubsetOf(vertexRoutes, destinationRoutes))
continue;
T2<Integer, Double> timeAndDistance = new T2<Integer, Double>(
(int) state.getElapsedTimeSeconds(), state.getWalkDistance());
timesByDestination.put(vertex, timeAndDistance);
}
}
}
return times;
}
private static <T> boolean isSubsetOf(Collection<T> c1, Collection<T> c2) {
for (T a : c1) {
if (!c2.contains(a)) {
return false;
}
}
return true;
}
class RouteAlight {
public StopProfile destinationProfiel;
public int stopNo;
}
private int[] computeMaxTransitData(RaptorData data, ArrayList<ServiceDay> serviceDays,
ArrayList<Vertex> destinations, HashMap<Vertex, T2<Integer, Double>>[] stopToStopWalk,
int startTime, int nRegions, int destinationRegion) {
HashMap<Vertex, StopProfile> stopProfile = new HashMap<Vertex, StopProfile>();
// initialize stop profiles for destination vertices
HashSet<Vertex> visitedLastRound = new HashSet<Vertex>();
for (Vertex v : destinations) {
stopProfile.put(v, new StopProfile(v, true));
visitedLastRound.add(v);
}
for (int round = 0; round < MAX_TRANSFERS; ++round) {
log.debug("round " + round + " from " + visitedLastRound.size());
HashSet<Vertex> visitedThisRound = new HashSet<Vertex>();
// transit phase
HashSet<StopProfile> newlyBoarded = new HashSet<StopProfile>();
for (RaptorRoute route : data.routes) {
boolean started = false;
List<RouteAlight> alightings = new ArrayList<RouteAlight>();
for (int stopNo = route.getNStops() - 1; stopNo >= 0; --stopNo) {
// try boarding here
RaptorStop stop = route.stops[stopNo];
TransitStop stopVertex = stop.stopVertex;
if (!started && !visitedLastRound.contains(stopVertex))
continue;
started = true;
StopProfile proflie = stopProfile.get(stopVertex);
if (proflie == null) {
proflie = new StopProfile(stopVertex);
stopProfile.put(stopVertex, proflie);
}
for (RouteAlight alight : alightings) {
if (proflie.transitTo(alight.destinationProfiel, route, stopNo,
alight.stopNo, serviceDays, startTime, round)) {
visitedThisRound.add(stopVertex);
newlyBoarded.add(proflie);
}
}
// try alighting here
if (visitedLastRound.contains(stopVertex)) {
final RouteAlight routeAlight = new RouteAlight();
routeAlight.stopNo = stopNo;
routeAlight.destinationProfiel = proflie;
alightings.add(routeAlight);
}
}
}
// walk phase
for (StopProfile profile : newlyBoarded) {
Vertex vertex = profile.vertex;
HashMap<Vertex, T2<Integer, Double>> nearbyStops = stopToStopWalk[vertex.getIndex()];
if (nearbyStops == null)
continue;
for (Map.Entry<Vertex, T2<Integer, Double>> nearbyStop : nearbyStops.entrySet()) {
Vertex nearbyVertex = nearbyStop.getKey();
// no need to walk from a stop to itself
if (nearbyVertex == vertex)
continue;
T2<Integer, Double> timeAndDistance = nearbyStop.getValue();
int time = timeAndDistance.getFirst();
double distance = timeAndDistance.getSecond();
StopProfile nearbyProfile = stopProfile.get(nearbyVertex);
if (nearbyProfile == null) {
nearbyProfile = new StopProfile(nearbyVertex);
stopProfile.put(nearbyVertex, nearbyProfile);
}
if (nearbyProfile.walkTo(profile, time, distance, round)) {
visitedThisRound.add(vertex);
}
}
}
visitedLastRound = visitedThisRound;
}
int[] timeForRegion = new int[nRegions];
for (RaptorStop stop : data.stops) {
TransitStop vertex = stop.stopVertex;
int region = vertex.getGroupIndex();
if (region < 0) {
log.warn("Missing region for " + vertex);
continue; // this should never happen
}
// don't worry about trips within this region
if (region == destinationRegion)
continue;
StopProfile profile = stopProfile.get(vertex);
if (profile == null) { // unreachable stop inside this region
timeForRegion[region] = Integer.MAX_VALUE;
} else {
int duration = profile.getMaxDuration(0, 86400 + 3600);
if (duration > timeForRegion[region]) {
timeForRegion[region] = duration;
}
}
}
return timeForRegion;
}
@SuppressWarnings("unchecked")
private RegionData makeRegionsBySubdivision(Graph graph, RaptorData data) {
ArrayList<Vertex> vertices = new ArrayList<Vertex>();
for (Vertex v : graph.getVertices()) {
if (!(v instanceof OnboardVertex)) {
vertices.add(v);
}
}
ArrayList<ArrayList<Vertex>> verticesForRegion = new ArrayList<ArrayList<Vertex>>();
int[] regionsForVertex = new int[AbstractVertex.getMaxIndex()];
Arrays.fill(regionsForVertex, -1);
int nRegions = split(verticesForRegion, regionsForVertex, vertices, 0, true, vertices.size() / N_REGIONS);
RegionData regions = new RegionData(regionsForVertex);
regions.minTime = new int[nRegions][nRegions];
regions.routes = new HashSet[nRegions][nRegions];
regions.stops = new HashSet[nRegions][nRegions];
for (int fromRegion = 0; fromRegion < nRegions; ++ fromRegion) {
for (int toRegion = 0; toRegion < nRegions; ++ toRegion) {
regions.routes[fromRegion][toRegion] = new HashSet<RaptorRoute>();
regions.stops[fromRegion][toRegion] = new HashSet<RaptorStop>();
}
}
regions.verticesForRegion = verticesForRegion;
return regions;
}
private void computeMinTimesAndInitialRoutes(Graph graph, RaptorData data) {
// now compute minTime for each region
final RegionData regions = data.regionData;
ArrayList<ArrayList<Vertex>> verticesForRegion = regions.verticesForRegion;
int regionIndex = 0;
for (ArrayList<Vertex> region : verticesForRegion) {
if (regionIndex % 5 == 0) {
log.debug("Building regions: " + regionIndex + " / " + verticesForRegion.size());
}
findMinTime(graph, data, regions, regionIndex, region);
findRoutes(graph, data, regions, regionIndex, region);
regionIndex += 1;
}
}
/**
* Find some routes used on trips starting from this region, and ending up at all other regions.
* This is optional, because we'll collect them at runtime otherwise
* @param graph
* @param data
* @param regions
* @param regionIndex
* @param region
*/
private void findRoutes(Graph graph, RaptorData data, RegionData regions, int regionIndex,
ArrayList<Vertex> region) {
Random random = new Random();
final HashSet<RaptorRoute>[] routes = regions.routes[regionIndex];
for (int j = 0; j < routes.length; ++j) {
routes[j] = new HashSet<RaptorRoute>();
}
final HashSet<RaptorStop>[] stops = regions.stops[regionIndex];
for (int j = 0; j < stops.length; ++j) {
stops[j] = new HashSet<RaptorStop>();
}
Raptor raptor = new Raptor();
int N_TRIPS = 5;
for (int i = 0; i < N_TRIPS; ++i) {
RoutingRequest options = new RoutingRequest();
graph.streetIndex = new StreetVertexIndexServiceImpl(graph);
int vertexNo = random.nextInt(region.size());
options.setRoutingContext(graph, region.get(vertexNo), null);
//assume everything is valid for one week
options.dateTime = (int)System.currentTimeMillis() / 1000 + random.nextInt(7*86400);
options.rctx.serviceDays = new ArrayList<ServiceDay>();
options.rctx.serviceDays.add(new ServiceDay.UniversalService(graph));
options.setMaxWalkDistance(MAX_DISTANCE);
options.setMaxTransfers(6);
RaptorStateSet states = raptor.getStateSet(options);
for (Entry<Vertex, List<RaptorState>> entry : states.getStates().entrySet()) {
Vertex v = entry.getKey();
int toRegion = regions.getRegionForVertex(v);
if (toRegion == -1) {
continue;
}
List<RaptorState> statesAtStop = entry.getValue();
for (RaptorState state : statesAtStop) {
while (state != null) {
RaptorRoute route = state.getRoute();
if (route != null)
routes[toRegion].add(route);
if (state.stop != null) {
stops[toRegion].add(state.stop);
}
state = state.getParent();
}
}
}
}
}
/*
@SuppressWarnings("unused")
private void findMinWalkDistance(RaptorData data, RegionData regions, int regionIndex,
ArrayList<Vertex> region) {
// find initial spt from all nodes in region
HashMap<Vertex, Double> distances = new HashMap<Vertex, Double>();
BinHeap<Vertex> queue = new BinHeap<Vertex>();
for (Vertex v : region) {
queue.insert(v, 0);
distances.put(v, 0.0);
}
// walk-distance free-transit spt computation
HashSet<Vertex> closed = new HashSet<Vertex>();
while (!queue.empty()) {
Vertex u = queue.extract_min();
if (closed.contains(u))
continue;
closed.add(u);
double distance = distances.get(u);
for (Edge e : u.getOutgoing()) {
if (!((e instanceof StreetEdge) || (e instanceof StreetTransitLink)))
continue;
double edgeDistance = e.getDistance() + distance;
Vertex v = e.getToVertex();
Double originalDistance = distances.get(v);
if (originalDistance == null || originalDistance > edgeDistance) {
distances.put(v, edgeDistance);
queue.insert(v, edgeDistance);
}
if (v instanceof TransitStop) {
RaptorStop stop = data.raptorStopsForStopId.get(((TransitStop) v).getStopId());
if (stop == null)
continue;
for (RaptorRoute route : data.routesForStop[stop.index]) {
for (RaptorStop stopOnRoute : route.stops) {
Vertex stopVertex = stopOnRoute.stopVertex;
originalDistance = distances.get(stopVertex);
if (originalDistance == null || originalDistance > edgeDistance) {
distances.put(stopVertex, edgeDistance);
queue.insert(stopVertex, edgeDistance);
}
}
}
}
}
}
final double[] minWalk = regions.minWalk[regionIndex];
Arrays.fill(minWalk, Double.MAX_VALUE);
for (Map.Entry<Vertex, Double> entry : distances.entrySet()) {
Vertex v = entry.getKey();
double distance = entry.getValue();
int toRegion = regionsForVertex[v.getIndex()];
if (toRegion == -1) {
System.out.println("Warning: no region for " + v);
continue;
}
if (minWalk[toRegion] > distance) {
minWalk[toRegion] = distance;
}
}
}
*/
private void findMinTime(Graph graph, RaptorData data, RegionData regions, int regionIndex,
ArrayList<Vertex> region) {
// find initial spt from all nodes in region
HashMap<Vertex, Integer> times = new HashMap<Vertex, Integer>();
BinHeap<Vertex> queue = new BinHeap<Vertex>();
for (Vertex v : region) {
queue.insert(v, 0);
times.put(v, 0);
}
RoutingRequest options = new RoutingRequest();
options.setWalkSpeed(6); // assume slightly fast biking speeds, which should be a good bound
// rctx ctor requires this
graph.streetIndex = new StreetVertexIndexServiceImpl(graph);
options.setRoutingContext(graph, region.get(0), null);
options.rctx.serviceDays = new ArrayList<ServiceDay>();
options.rctx.serviceDays.add(new ServiceDay.UniversalService(graph));
HashSet<Vertex> closed = new HashSet<Vertex>();
while (!queue.empty()) {
Vertex u = queue.extract_min();
if (closed.contains(u))
continue;
closed.add(u);
int time = times.get(u);
for (Edge e : u.getOutgoing()) {
final double timeLowerBound = e.timeLowerBound(options);
if (Double.isNaN(timeLowerBound))
continue;
int edgeTime = (int) (timeLowerBound + time);
Vertex v = e.getToVertex();
Integer originalTime = times.get(v);
if (originalTime == null || originalTime > edgeTime) {
times.put(v, edgeTime);
queue.insert(v, (double) edgeTime);
}
}
}
final int[] minTime = regions.minTime[regionIndex];
Arrays.fill(minTime, Integer.MAX_VALUE);
for (Map.Entry<Vertex, Integer> entry : times.entrySet()) {
Vertex v = entry.getKey();
int distance = entry.getValue();
// int toRegion = regions.regionForVertex[v.getIndex()];
int toRegion = regions.getRegionForVertex(v);
if (toRegion == -1) {
continue;
}
if (minTime[toRegion] > distance) {
minTime[toRegion] = distance;
}
}
}
class HorizontalVertexComparator implements Comparator<Vertex> {
@Override
public int compare(Vertex o1, Vertex o2) {
double cmp = o1.getCoordinate().x - o2.getCoordinate().x;
if (cmp == 0) {
return 0;
}
return cmp > 0 ? 1 : -1;
}
}
class VerticalVertexComparator implements Comparator<Vertex> {
@Override
public int compare(Vertex o1, Vertex o2) {
double cmp = o1.getCoordinate().y - o2.getCoordinate().y;
if (cmp == 0) {
return 0;
}
return cmp > 0 ? 1 : -1;
}
}
private int split(ArrayList<ArrayList<Vertex>> vertexForRegion, int[] regionsForVertex, List<Vertex> vertices,
int index, boolean horiz, int regionSize) {
if (vertices.size() <= regionSize) {
final ArrayList<Vertex> region = new ArrayList<Vertex>();
vertexForRegion.add(region);
for (Vertex vertex : vertices) {
if (regionsForVertex == null) {
vertex.setGroupIndex(index);
} else {
regionsForVertex[vertex.getIndex()] = index;
vertex.setGroupIndex(index);
}
region.add(vertex);
}
return index + 1;
}
Comparator<Vertex> comparator = horiz ? new HorizontalVertexComparator()
: new VerticalVertexComparator();
Collections.sort(vertices, comparator);
int mid = vertices.size() / 2;
Coordinate last = vertices.get(mid - 1).getCoordinate();
// we don't want to split two vertices with the same coordinate into different
// regions, so move mid up until it includes all vertices with the last coordinate
for (; mid < vertices.size(); ++mid) {
if (!vertices.get(mid).getCoordinate().equals(last)) {
break;
}
}
// this split is too uneven -- just go ahead and make it one region
if (mid > vertices.size() * 3 / 4) {
final ArrayList<Vertex> region = new ArrayList<Vertex>();
vertexForRegion.add(region);
for (Vertex vertex : vertices) {
if (regionsForVertex == null) {
vertex.setGroupIndex(index);
} else {
regionsForVertex[vertex.getIndex()] = index;
vertex.setGroupIndex(index);
}
region.add(vertex);
}
return index + 1;
}
index = split(vertexForRegion, regionsForVertex, vertices.subList(0, mid), index, !horiz, regionSize);
index = split(vertexForRegion, regionsForVertex, vertices.subList(mid, vertices.size()), index, !horiz,
regionSize);
return index;
}
private RaptorStop makeRaptorStop(RaptorData data, Stop stop) {
RaptorStop rs = data.raptorStopsForStopId.get(stop.getId());
if (rs == null) {
rs = new RaptorStop();
rs.index = data.raptorStopsForStopId.size();
data.stops[rs.index] = rs;
data.raptorStopsForStopId.put(stop.getId(), rs);
}
return rs;
}
// this doesn't speed things up
@SuppressWarnings({ "unchecked", "unused" })
private void initNearbyStops(RaptorData data) {
final int nTotalStops = data.stops.length;
data.nearbyStops = new List[nTotalStops];
for (int i = 0; i < nTotalStops; ++i) {
if (i % 500 == 0) {
System.out.println("Precomputing nearby stops:" + i + " / " + nTotalStops);
}
data.nearbyStops[i] = new ArrayList<T2<Double, RaptorStop>>();
RaptorStop stop = data.stops[i];
Coordinate coord = stop.stopVertex.getCoordinate();
for (RaptorStop other : data.stops) {
if (other == stop)
continue;
Coordinate otherCoord = other.stopVertex.getCoordinate();
if (Math.abs(otherCoord.x - coord.x) > 4850 / 111111.0) {
continue;
}
if (Math.abs(otherCoord.y - coord.y) > 4850 / 111111.0) {
continue;
}
double distance = distanceLibrary.fastDistance(coord, otherCoord);
if (distance > 4850) // 3 mi
continue;
data.nearbyStops[i].add(new T2<Double, RaptorStop>(distance, other));
}
Collections.sort(data.nearbyStops[i], new Comparator<T2<Double, RaptorStop>>() {
@Override
public int compare(T2<Double, RaptorStop> arg0, T2<Double, RaptorStop> arg1) {
return (int) Math.signum(arg0.getFirst() - arg1.getFirst());
}
});
}
}
@Override
public List<String> provides() {
return Collections.emptyList();
}
@Override
public List<String> getPrerequisites() {
return Arrays.asList("transitIndex");
}
@Override
public void checkInputs() {
}
}