/** * Finds all egress paths from to coordinate to end stop and adds routers to egressRouter * * @param request * @param egressRouter */ private void findEgressPaths(ProfileRequest request, Map<LegMode, StreetRouter> egressRouter) { // For egress // TODO: this must be reverse search for (LegMode mode : request.egressModes) { StreetRouter streetRouter = new StreetRouter(transportNetwork.streetLayer); streetRouter.transitStopSearch = true; streetRouter.dominanceVariable = StreetRouter.State.RoutingVariable.DURATION_SECONDS; if (egressUnsupportedModes.contains(mode)) { continue; } // TODO: add support for bike sharing streetRouter.streetMode = StreetMode.valueOf(mode.toString()); streetRouter.profileRequest = request; streetRouter.timeLimitSeconds = request.getTimeLimit(mode); if (streetRouter.setOrigin(request.toLat, request.toLon)) { streetRouter.route(); TIntIntMap stops = streetRouter.getReachedStops(); egressRouter.put(mode, streetRouter); LOG.info("Added {} edgres stops for mode {}", stops.size(), mode); } else { LOG.warn( "MODE:{}, Edge near the origin coordinate wasn't found. Routing didn't start!", mode); } } }
/** * Finds access paths from from coordinate in request and adds all routers with paths to * accessRouter map * * @param request * @param accessRouter */ private void findAccessPaths(ProfileRequest request, Map<LegMode, StreetRouter> accessRouter) { // Routes all access modes for (LegMode mode : request.accessModes) { StreetRouter streetRouter = new StreetRouter(transportNetwork.streetLayer); streetRouter.profileRequest = request; if (mode == LegMode.CAR_PARK) { streetRouter = findParkRidePath(request, streetRouter); if (streetRouter != null) { accessRouter.put(LegMode.CAR_PARK, streetRouter); } else { LOG.warn( "MODE:{}, Edge near the origin coordinate wasn't found. Routing didn't start!", mode); } } else if (mode == LegMode.BICYCLE_RENT) { if (!transportNetwork.streetLayer.bikeSharing) { LOG.warn("Bike sharing trip requested but no bike sharing stations in the streetlayer"); continue; } streetRouter = findBikeRentalPath(request, streetRouter, false); if (streetRouter != null) { accessRouter.put(LegMode.BICYCLE_RENT, streetRouter); } else { LOG.warn("Not found path from cycle to end"); } } else { streetRouter.streetMode = StreetMode.valueOf(mode.toString()); // Gets correct maxCar/Bike/Walk time in seconds for access leg based on mode since it // depends on the mode streetRouter.timeLimitSeconds = request.getTimeLimit(mode); streetRouter.transitStopSearch = true; streetRouter.dominanceVariable = StreetRouter.State.RoutingVariable.DURATION_SECONDS; if (streetRouter.setOrigin(request.fromLat, request.fromLon)) { streetRouter.route(); // Searching for access paths accessRouter.put(mode, streetRouter); } else { LOG.warn( "MODE:{}, Edge near the origin coordinate wasn't found. Routing didn't start!", mode); } } } }
/** * Combine the results of several street searches using different modes into a single map It also * saves with which mode was stop reached into stopModeMap. This map is then used to create * itineraries in response */ private TIntIntMap combineMultimodalRoutingAccessTimes( Map<LegMode, StreetRouter> routers, TIntObjectMap<LegMode> stopModeMap, ProfileRequest request) { // times at transit stops TIntIntMap times = new TIntIntHashMap(); // weights at transit stops TIntIntMap weights = new TIntIntHashMap(); for (Map.Entry<LegMode, StreetRouter> entry : routers.entrySet()) { int maxTime = 30; int minTime = 0; int penalty = 0; LegMode mode = entry.getKey(); switch (mode) { case BICYCLE: maxTime = request.maxBikeTime; minTime = request.minBikeTime; penalty = BIKE_PENALTY; break; case BICYCLE_RENT: // TODO this is not strictly correct, bike rent is partly walking maxTime = request.maxBikeTime; minTime = request.minBikeTime; penalty = BIKESHARE_PENALTY; break; case WALK: maxTime = request.maxWalkTime; break; case CAR: // TODO this is not strictly correct, CAR PARK is partly walking case CAR_PARK: maxTime = request.maxCarTime; minTime = request.minCarTime; penalty = CAR_PENALTY; break; } maxTime *= 60; // convert to seconds minTime *= 60; // convert to seconds final int maxTimeFinal = maxTime; final int minTimeFinal = minTime; final int penaltyFinal = penalty; StreetRouter router = entry.getValue(); router .getReachedStops() .forEachEntry( (stop, time) -> { if (time > maxTimeFinal || time < minTimeFinal) return true; // Skip stops that can't be used with wheelchairs if wheelchair routing is requested if (request.wheelchair && !transportNetwork.transitLayer.stopsWheelchair.get(stop)) { return true; } int weight = time + penaltyFinal; // There are penalties for using certain modes, to avoid bike/car trips that are // only marginally faster // than walking, so we use weights to decide which mode "wins" to access a // particular stop. if (!weights.containsKey(stop) || weight < weights.get(stop)) { times.put(stop, time); weights.put(stop, weight); stopModeMap.put(stop, mode); } return true; // iteration should continue }); } // we don't want to explore a boatload of access/egress stops. Pick only the closest several // hundred. // What this means is that in urban environments you'll get on the bus nearby, in suburban // environments // you may walk/bike/drive a very long way. // NB in testing it's not clear this actually does a lot for performance, maybe 1-1.5s int stopsFound = times.size(); if (stopsFound > MAX_ACCESS_STOPS) { TIntList timeList = new TIntArrayList(); times.forEachValue(timeList::add); timeList.sort(); // This gets last time in timeList int cutoff = timeList.get( MAX_ACCESS_STOPS); // it needs to be same as MAX_ACCESS_STOPS since if there are // minimally MAX_ACCESS_STOPS + 1 stops the indexes are from // 0-MAX_ACCESS_STOPS for (TIntIntIterator it = times.iterator(); it.hasNext(); ) { it.advance(); if (it.value() > cutoff) it.remove(); } LOG.warn("{} stops found, using {} nearest", stopsFound, times.size()); } else { LOG.info("{} stops found", stopsFound); } // return the times, not the weights return times; }