/* *********************************************************************** * * project: org.matsim.* * *********************************************************************** * * * * copyright : (C) 2016 by the members listed in the COPYING, * * LICENSE and WARRANTY file. * * email : info at matsim dot org * * * * *********************************************************************** * * * * 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 2 of the License, or * * (at your option) any later version. * * See also COPYING, LICENSE and WARRANTY file * * * * *********************************************************************** */ package org.matsim.pt2matsim.mapping; import org.apache.log4j.Logger; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.core.utils.misc.Counter; import org.matsim.pt.transitSchedule.api.TransitLine; import org.matsim.pt.transitSchedule.api.TransitRoute; import org.matsim.pt.transitSchedule.api.TransitRouteStop; import org.matsim.pt2matsim.mapping.linkCandidateCreation.LinkCandidate; import org.matsim.pt2matsim.mapping.linkCandidateCreation.LinkCandidateCreator; import org.matsim.pt2matsim.mapping.networkRouter.ScheduleRouters; import org.matsim.pt2matsim.mapping.networkRouter.ScheduleRoutersFactory; import org.matsim.pt2matsim.mapping.pseudoRouter.*; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Generates and calculates the pseudoRoutes for all the queued * transit lines. If no route on the network can be found (or the * scheduleTransportMode should not be mapped to the network), artificial * links between link candidates are stored to be created later. * * @author polettif */ public class PseudoRoutingImpl implements PseudoRouting { protected static Logger log = Logger.getLogger(PseudoRoutingImpl.class); private final Progress progress; private static boolean warnMinTravelCost = true; private final LinkCandidateCreator linkCandidates; private final ScheduleRoutersFactory scheduleRoutersFactory; private final List<TransitLine> queue = new ArrayList<>(); private final Set<ArtificialLink> necessaryArtificialLinks = new HashSet<>(); private final PseudoSchedule threadPseudoSchedule = new PseudoScheduleImpl(); private double maxTravelCostFactor; public PseudoRoutingImpl(ScheduleRoutersFactory scheduleRoutersFactory, LinkCandidateCreator linkCandidates, double maxTravelCostFactor, Progress progress) { this.maxTravelCostFactor = maxTravelCostFactor; this.scheduleRoutersFactory = scheduleRoutersFactory; this.linkCandidates = linkCandidates; this.progress = progress; } @Override public void addTransitLineToQueue(TransitLine transitLine) { queue.add(transitLine); } @Override public void run() { ScheduleRouters scheduleRouters = scheduleRoutersFactory.createInstance(); for(TransitLine transitLine : queue) { for(TransitRoute transitRoute : transitLine.getRoutes().values()) { /* [1] Initiate pseudoGraph and Dijkstra algorithm for the current transitRoute. In the pseudoGraph, all link candidates are represented as nodes and the network paths between link candidates are reduced to a representation edge only storing the travel cost. With the pseudoGraph, the best linkCandidate sequence can be calculated (using Dijkstra). From this sequence, the actual path on the network can be routed later on. */ PseudoGraph pseudoGraph = new PseudoGraphImpl(); /* [2] Calculate the shortest paths between each pair of routeStops/ParentStopFacility */ List<TransitRouteStop> routeStops = transitRoute.getStops(); for(int i = 0; i < routeStops.size() - 1; i++) { Set<LinkCandidate> linkCandidatesCurrent = linkCandidates.getLinkCandidates(routeStops.get(i), transitLine, transitRoute); Set<LinkCandidate> linkCandidatesNext = linkCandidates.getLinkCandidates(routeStops.get(i + 1), transitLine, transitRoute); double minTravelCost = scheduleRouters.getMinimalTravelCost(routeStops.get(i), routeStops.get(i + 1), transitLine, transitRoute); double maxAllowedTravelCost = minTravelCost * maxTravelCostFactor; if(minTravelCost == 0 && warnMinTravelCost) { log.warn("There are stop pairs where minTravelCost is 0.0! This might happen if two stops are on the same coordinate or if departure and arrival time of two subsequent stops are identical. Further messages are suppressed."); warnMinTravelCost = false; } /* [3] Calculate the shortest path between all link candidates. */ for(LinkCandidate linkCandidateCurrent : linkCandidatesCurrent) { for(LinkCandidate linkCandidateNext : linkCandidatesNext) { boolean useExistingNetworkLinks = false; double pathCost = 2 * maxAllowedTravelCost; List<Link> pathLinks = null; /* [3.1] If one or both link candidates are loop links we don't have to search a least cost path on the network. */ if(!linkCandidateCurrent.isLoopLink() && !linkCandidateNext.isLoopLink()) { /* Calculate the least cost path on the network */ LeastCostPathCalculator.Path leastCostPath = scheduleRouters.calcLeastCostPath(linkCandidateCurrent, linkCandidateNext, transitLine, transitRoute); if(leastCostPath != null) { pathCost = leastCostPath.travelCost; pathLinks = leastCostPath.links; // if both link candidates are the same, cost should get higher if(linkCandidateCurrent.getLink().getId().equals(linkCandidateNext.getLink().getId())) { pathCost *= 4; } } useExistingNetworkLinks = pathCost < maxAllowedTravelCost; } /* [3.2] If a path on the network could be found and its travel cost are below maxAllowedTravelCost, a normal edge is added to the pseudoGraph */ if(useExistingNetworkLinks) { double currentCandidateTravelCost = scheduleRouters.getLinkCandidateTravelCost(linkCandidateCurrent); double nextCandidateTravelCost = scheduleRouters.getLinkCandidateTravelCost(linkCandidateNext); double edgeWeight = pathCost + 0.5 * currentCandidateTravelCost + 0.5 * nextCandidateTravelCost; pseudoGraph.addEdge(i, routeStops.get(i), linkCandidateCurrent, routeStops.get(i + 1), linkCandidateNext, edgeWeight, pathLinks); } /* [3.2] Create artificial links between two routeStops if: - no path on the network could be found - the travel cost of the path are greater than maxAllowedTravelCost Artificial links are created between all LinkCandidates (usually this means between one dummy link for the stop facility and the other linkCandidates). */ else { double currentCandidateTravelCost = scheduleRouters.getLinkCandidateTravelCost(linkCandidateCurrent); double nextCandidateTravelCost = scheduleRouters.getLinkCandidateTravelCost(linkCandidateNext); double artificialEdgeWeight = maxAllowedTravelCost - 0.5 * currentCandidateTravelCost - 0.5 * nextCandidateTravelCost; pseudoGraph.addEdge(i, routeStops.get(i), linkCandidateCurrent, routeStops.get(i + 1), linkCandidateNext, artificialEdgeWeight, null); } } } } // - routeStop loop /* [4] Finish the pseudoGraph by adding dummy nodes. */ pseudoGraph.addDummyEdges(routeStops, linkCandidates.getLinkCandidates(routeStops.get(0), transitLine, transitRoute), linkCandidates.getLinkCandidates(routeStops.get(routeStops.size() - 1), transitLine, transitRoute)); /* [5] Find the least cost path i.e. the PseudoRouteStop sequence */ List<PseudoRouteStop> pseudoPath = pseudoGraph.getLeastCostStopSequence(); if(pseudoPath == null) { throw new RuntimeException("PseudoGraph has no path from SOURCE to DESTINATION for transit route " + transitRoute.getId() + " " + "on line " + transitLine.getId() + " from \"" + routeStops.get(0).getStopFacility().getName() + "\" " + "to \"" + routeStops.get(routeStops.size() - 1).getStopFacility().getName() + "\""); } else { necessaryArtificialLinks.addAll(pseudoGraph.getArtificialNetworkLinks()); threadPseudoSchedule.addPseudoRoute(transitLine, transitRoute, pseudoPath, pseudoGraph.getNetworkLinkIds()); } progress.update(); } } } /** * @return a pseudo schedule generated during run() */ @Override public PseudoSchedule getPseudoSchedule() { return threadPseudoSchedule; } /** * Adds the artificial links to the network. * * Not thread safe. */ @Override public void addArtificialLinks(Network network) { for(ArtificialLink a : necessaryArtificialLinks) { if(!network.getLinks().containsKey(a.getId())) { network.addLink(a); } } } }