/* 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.edgetype.loader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import org.opentripplanner.common.IterableLibrary;
import org.opentripplanner.common.model.P2;
import org.opentripplanner.gbannotation.BikeRentalStationUnlinked;
import org.opentripplanner.gbannotation.StopUnlinked;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.edgetype.PlainStreetEdge;
import org.opentripplanner.routing.edgetype.StreetEdge;
import org.opentripplanner.routing.edgetype.StreetTransitLink;
import org.opentripplanner.routing.edgetype.factory.FindMaxWalkDistances;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.vertextype.BikeRentalStationVertex;
import org.opentripplanner.routing.vertextype.TransitStop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NetworkLinker {
private static Logger LOG = LoggerFactory.getLogger(NetworkLinker.class);
private Graph graph;
private NetworkLinkerLibrary networkLinkerLibrary;
public NetworkLinker(Graph graph, HashMap<Class<?>,Object> extra) {
this.graph = graph;
this.networkLinkerLibrary = new NetworkLinkerLibrary(graph, extra);
networkLinkerLibrary.options = new RoutingRequest(TraverseMode.BICYCLE);
}
public NetworkLinker(Graph graph) {
// we should be using Collections.emptyMap(), but it breaks Java's broken-ass type checker
this(graph, new HashMap<Class<?>, Object>());
}
/**
* Link the transit network to the street network. Connect each transit vertex to the nearest
* Street edge with a StreetTransitLink.
*
* @param index
*/
public void createLinkage() {
LOG.debug("creating linkages...");
// iterate over a copy of vertex list because it will be modified
ArrayList<Vertex> vertices = new ArrayList<Vertex>();
vertices.addAll(graph.getVertices());
for (TransitStop ts : IterableLibrary.filter(vertices, TransitStop.class)) {
// if the street is already linked there is no need to linked it again,
// could happened if using the prune isolated island
boolean alreadyLinked = false;
for(Edge e:ts.getOutgoing()){
if(e instanceof StreetTransitLink) {
alreadyLinked = true;
break;
}
}
if(alreadyLinked) continue;
// only connect transit stops that (a) are entrances, or (b) have no associated
// entrances
if (ts.isEntrance() || !ts.hasEntrances()) {
boolean wheelchairAccessible = ts.hasWheelchairEntrance();
if (!networkLinkerLibrary.connectVertexToStreets(ts, wheelchairAccessible).getResult()) {
LOG.warn(graph.addBuilderAnnotation(new StopUnlinked(ts)));
}
}
}
//remove replaced edges
for (HashSet<StreetEdge> toRemove : networkLinkerLibrary.replacements.keySet()) {
for (StreetEdge edge : toRemove) {
edge.getFromVertex().removeOutgoing(edge);
edge.getToVertex().removeIncoming(edge);
}
}
//and add back in replacements
for (LinkedList<P2<PlainStreetEdge>> toAdd : networkLinkerLibrary.replacements.values()) {
for (P2<PlainStreetEdge> edges : toAdd) {
PlainStreetEdge edge1 = edges.getFirst();
if (edge1.getToVertex().getLabel().startsWith("split ") || edge1.getFromVertex().getLabel().startsWith("split ")) {
continue;
}
edge1.getFromVertex().addOutgoing(edge1);
edge1.getToVertex().addIncoming(edge1);
PlainStreetEdge edge2 = edges.getSecond();
if (edge2 != null) {
edge2.getFromVertex().addOutgoing(edge2);
edge2.getToVertex().addIncoming(edge2);
}
}
}
// Do we really need this? Commenting out does seem to cause some slowdown. (AMB)
networkLinkerLibrary.markLocalStops();
FindMaxWalkDistances.find(graph);
LOG.debug("Linking bike rental stations...");
for (BikeRentalStationVertex brsv : IterableLibrary.filter(vertices,
BikeRentalStationVertex.class)) {
if (!networkLinkerLibrary.connectVertexToStreets(brsv).getResult()) {
LOG.warn(graph.addBuilderAnnotation(new BikeRentalStationUnlinked(brsv)));
}
}
}
}