/* 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.routing.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.edgetype.LegSwitchingEdge;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.spt.GraphPath;
/**
* The Traveling Salesman Problem
* Used in searches with 'intermediates'
*/
public class TSPPathFinder {
/**
* A TSPPath is an ordered list of endpoints with a total cost to visit those endpoints
* @author novalis
*
*/
static class TSPPath implements Cloneable {
List<Vertex> vertices;
double cost;
TSPPath(Vertex v, double cost) {
vertices = new ArrayList<Vertex>();
vertices.add(v);
this.cost = cost;
}
public TSPPath(TSPPath tspPath) {
vertices = new ArrayList<Vertex>(tspPath.vertices);
cost = tspPath.cost;
}
public void addVertex(Vertex v, double cost) {
this.vertices.add(v);
this.cost += cost;
}
public TSPPath clone() {
return new TSPPath(this);
}
}
public TSPPathFinder() {}
private static TSPPath findShortestPathInternal(Vertex toVertex, Vertex fromVertex,
Map<Vertex, HashMap<Vertex, GraphPath>> paths, Collection<Vertex> intermediates, double costSoFar) {
if (intermediates.size() == 0) {
//base case: simply the path from the fromVertex to the toVertex
TSPPath path = new TSPPath(toVertex, (paths.get(fromVertex).get(toVertex)).getWeight());
return path;
}
List<Vertex> reducedIntermediates = new ArrayList<Vertex> ();
reducedIntermediates.addAll(intermediates);
TSPPath shortest = null;
//find all paths through the remaining intermediate vertices, considering this as the start
for (Vertex vertex : intermediates) {
reducedIntermediates.remove(vertex);
TSPPath path = findShortestPathInternal(toVertex, vertex, paths, reducedIntermediates,
costSoFar + paths.get(fromVertex).get(vertex).getWeight());
if (shortest == null || shortest.cost > path.cost) {
shortest = path;
path.vertices.add(0, vertex);
}
reducedIntermediates.add(vertex);
}
return shortest;
}
public static GraphPath findShortestPath(Vertex toVertex, Vertex fromVertex,
Map<Vertex, HashMap<Vertex, GraphPath>> paths, HashSet<Vertex> vertices, long time, RoutingRequest options) {
TSPPath shortestPath = findShortestPathInternal(toVertex, fromVertex, paths, vertices, 0);
Vertex firstIntermediate = shortestPath.vertices.get(0);
HashMap<Vertex, GraphPath> pathsFromFV = paths.get(fromVertex);
//get the path from the end of the first subpath
GraphPath newPath = new GraphPath(pathsFromFV.get(firstIntermediate).states.getLast(), false);
Vertex lastVertex = firstIntermediate;
for (Vertex v : shortestPath.vertices.subList(1, shortestPath.vertices.size())) {
State lastState = newPath.states.getLast();
GraphPath subPath = paths.get(lastVertex).get(v);
//add a leg-switching state
LegSwitchingEdge legSwitchingEdge = new LegSwitchingEdge(lastVertex, lastVertex);
lastState = legSwitchingEdge.traverse(lastState);
newPath.edges.add(legSwitchingEdge);
newPath.states.add(lastState);
//add the next subpath
for (Edge e : subPath.edges) {
lastState = e.traverse(lastState);
newPath.edges.add(e);
newPath.states.add(lastState);
}
lastVertex = v;
}
return newPath;
}
}