Skip to content

Commit

Permalink
Use new SimpleStreetLinker on origin/destination
Browse files Browse the repository at this point in the history
Working on #2267. Linking works but code is a little messy.
Currently one temporary vertex is created when closest thing is
existing from/to vertex. And a second split vertex is created if
the closest edge is a street which is splitted. All new edges and
vertices are temporary and are removed when thread finishes.
  • Loading branch information
buma committed May 25, 2016
1 parent 02a667a commit 7480a51
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 55 deletions.
@@ -0,0 +1,94 @@
package org.opentripplanner.graph_builder.linking;

import com.vividsolutions.jts.geom.Coordinate;
import org.apache.lucene.store.TrackingDirectoryWrapper;
import org.opentripplanner.common.model.GenericLocation;
import org.opentripplanner.common.model.P2;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.TraverseModeSet;
import org.opentripplanner.routing.edgetype.StreetEdge;
import org.opentripplanner.routing.edgetype.TemporaryFreeEdge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.location.TemporaryStreetLocation;
import org.opentripplanner.routing.vertextype.StreetVertex;
import org.opentripplanner.util.NonLocalizedString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Linking seems to work.
*
*
* Created by mabu on 20.5.2016.
*/
public class OriginDestinationLinker extends SimpleStreetSplitter {
private static final Logger LOG = LoggerFactory.getLogger(OriginDestinationLinker.class);
/**
* Construct a new SimpleStreetSplitter. Be aware that only one SimpleStreetSplitter should be
* active on a graph at any given time.
*
* @param graph
*/
public OriginDestinationLinker(Graph graph) {
super(graph);
}

public Vertex getClosestVertex(GenericLocation location, RoutingRequest options,
boolean endVertex) {
if (endVertex) {
LOG.debug("Finding end vertex for {}", location);
} else {
LOG.debug("Finding start vertex for {}", location);
}
Coordinate coord = location.getCoordinate();
//TODO: add nice name
TemporaryStreetLocation closest = new TemporaryStreetLocation(
"corner " + Math.random(), coord, new NonLocalizedString("generated point"), endVertex);

TraverseModeSet modes = options.modes;
TraverseMode nonTransitMode = TraverseMode.WALK;
if (modes.getCar())
nonTransitMode = TraverseMode.CAR;
else if (modes.getWalk())
nonTransitMode = TraverseMode.WALK;
else if (modes.getBicycle())
nonTransitMode = TraverseMode.BICYCLE;

if(!link(closest, nonTransitMode)) {
LOG.warn("Couldn't link {}", location);
}
return closest;

}

/**
* Make the appropriate type of link edges from a vertex
*
* @param from
* @param to
*/
@Override
protected void makeLinkEdges(Vertex from, StreetVertex to) {
TemporaryStreetLocation tse = (TemporaryStreetLocation) from;
if (tse.isEndVertex()) {
LOG.debug("Linking end vertex to {} -> {}", to, tse);
new TemporaryFreeEdge(to, tse);
} else {
LOG.debug("Linking start vertex to {} -> {}", tse, to);
new TemporaryFreeEdge(tse, to);
}
}

@Override
protected void removeOriginalEdge(StreetEdge edge) {
//Intentionally empty since we are creating temporary edges which should not change the graph
//and they are removed anyway when thread is disposed
}

@Override
protected void updateIndex(P2<StreetEdge> edges) {
//Intentionally empty since we are creating temporary edges which should not change the graph
//and they are removed anyway when thread is disposed
}
}
Expand Up @@ -31,6 +31,8 @@
import org.opentripplanner.routing.vertextype.BikeRentalStationVertex;
import org.opentripplanner.routing.vertextype.SplitterVertex;
import org.opentripplanner.routing.vertextype.StreetVertex;
import org.opentripplanner.routing.vertextype.TemporarySplitterVertex;
import org.opentripplanner.routing.vertextype.TemporaryVertex;
import org.opentripplanner.routing.vertextype.TransitStop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -101,8 +103,13 @@ else if (v instanceof BikeParkVertex)
}
}

/** Link this vertex into the graph */
/** Link this vertex into the graph to the closest walkable edge */
public boolean link (Vertex vertex) {
return link(vertex, TraverseMode.WALK);
}

/** Link this vertex into the graph */
public boolean link(Vertex vertex, TraverseMode traverseMode) {
// find nearby street edges
// TODO: we used to use an expanding-envelope search, which is more efficient in
// dense areas. but first let's see how inefficient this is. I suspect it's not too
Expand Down Expand Up @@ -130,7 +137,7 @@ public boolean link (Vertex vertex) {
public boolean apply(StreetEdge edge) {
// note: not filtering by radius here as distance calculation is expensive
// we do that below.
return edge.canTraverse(new TraverseModeSet(TraverseMode.WALK)) &&
return edge.canTraverse(new TraverseModeSet(traverseMode)) &&
// only link to edges still in the graph.
edge.getToVertex().getIncoming().contains(edge);
}
Expand Down Expand Up @@ -209,43 +216,76 @@ else if (ll.getSegmentIndex() == orig.getNumPoints() - 2 && ll.getSegmentFractio
makeLinkEdges(tstop, (StreetVertex) edge.getToVertex());
}

else {
else {

TemporaryVertex temporaryVertex = null;
boolean endVertex = false;
if (tstop instanceof TemporaryVertex) {
temporaryVertex = (TemporaryVertex) tstop;
endVertex = temporaryVertex.isEndVertex();

}
// split the edge, get the split vertex
SplitterVertex v0 = split(edge, ll);
SplitterVertex v0 = split(edge, ll, temporaryVertex != null, endVertex);
makeLinkEdges(tstop, v0);
}
}

/** Split the street edge at the given fraction */
private SplitterVertex split (StreetEdge edge, LinearLocation ll) {

/**
* Split the street edge at the given fraction
*
* @param edge to be split
* @param ll fraction at which to split the edge
* @param temporarySplit if true this is temporary split at origin/destinations search and only temporary edges vertices are created
* @param endVertex if this is temporary edge this is true if this is end vertex otherwise it doesn't matter
* @return Splitter vertex with added new edges
*/
private SplitterVertex split (StreetEdge edge, LinearLocation ll, boolean temporarySplit, boolean endVertex) {
LineString geometry = edge.getGeometry();

// create the geometries
Coordinate splitPoint = ll.getCoordinate(geometry);

// every edge can be split exactly once, so this is a valid label
SplitterVertex v = new SplitterVertex(graph, "split from " + edge.getId(), splitPoint.x, splitPoint.y, edge);
SplitterVertex v;
if (temporarySplit) {
v = new TemporarySplitterVertex(graph, "split from " + edge.getId(), splitPoint.x, splitPoint.y,
edge, endVertex);
} else {
v = new SplitterVertex(graph, "split from " + edge.getId(), splitPoint.x, splitPoint.y,
edge);
}

// make the edges
// TODO this is using the StreetEdge implementation of split, which will discard elevation information
// on edges that have it
P2<StreetEdge> edges = edge.split(v);
P2<StreetEdge> edges = edge.split(v, !temporarySplit);

// update indices
idx.insert(edges.first.getGeometry().getEnvelopeInternal(), edges.first);
idx.insert(edges.second.getGeometry().getEnvelopeInternal(), edges.second);
//this functions are created so they can be overridden in OriginDestinationLinker where they should do nothing.
updateIndex(edges);

// (no need to remove original edge, we filter it when it comes out of the index)
removeOriginalEdge(edge);

return v;
}

// remove original edge
protected void removeOriginalEdge(StreetEdge edge) {
// remove original edge from the graph
edge.getToVertex().removeIncoming(edge);
edge.getFromVertex().removeOutgoing(edge);
}

return v;
protected void updateIndex(P2<StreetEdge> edges) {
// update indices of new edges
idx.insert(edges.first.getGeometry().getEnvelopeInternal(), edges.first);
idx.insert(edges.second.getGeometry().getEnvelopeInternal(), edges.second);

// (no need to remove original edge, we filter it when it comes out of the index)
}

/** Make the appropriate type of link edges from a vertex */
private void makeLinkEdges (Vertex from, StreetVertex to) {
protected void makeLinkEdges (Vertex from, StreetVertex to) {
if (from instanceof TransitStop)
makeTransitLinkEdges((TransitStop) from, to);
else if (from instanceof BikeRentalStationVertex)
Expand Down
97 changes: 60 additions & 37 deletions src/main/java/org/opentripplanner/routing/edgetype/StreetEdge.java
Expand Up @@ -29,6 +29,7 @@ the License, or (at your option) any later version.
import org.opentripplanner.routing.vertextype.OsmVertex;
import org.opentripplanner.routing.vertextype.SplitterVertex;
import org.opentripplanner.routing.vertextype.StreetVertex;
import org.opentripplanner.routing.vertextype.TemporarySplitterVertex;
import org.opentripplanner.traffic.StreetSpeedSnapshot;
import org.opentripplanner.util.BitSetUtils;
import org.opentripplanner.util.I18NString;
Expand Down Expand Up @@ -797,48 +798,70 @@ protected void calculateLengthFromGeometry () {
}

/** Split this street edge and return the resulting street edges */
public P2<StreetEdge> split (SplitterVertex v) {
public P2<StreetEdge> split(SplitterVertex v, boolean destructive) {
P2<LineString> geoms = GeometryUtils.splitGeometryAtPoint(getGeometry(), v.getCoordinate());
StreetEdge e1 = new StreetEdge((StreetVertex) fromv, v, geoms.first, name, 0, permission, this.isBack());
StreetEdge e2 = new StreetEdge(v, (StreetVertex) tov, geoms.second, name, 0, permission, this.isBack());

// figure the lengths, ensuring that they sum to the length of this edge
e1.calculateLengthFromGeometry();
e2.calculateLengthFromGeometry();

// we have this code implemented in both directions, because splits are fudged half a millimeter
// when the length of this is odd. We want to make sure the lengths of the split streets end up
// exactly the same as their backStreets so that if they are split again the error does not accumulate
// and so that the order in which they are split does not matter.
if (!isBack()) {
// cast before the divide so that the sum is promoted
double frac = (double) e1.length_mm / (e1.length_mm + e2.length_mm);
e1.length_mm = (int) (length_mm * frac);
e2.length_mm = length_mm - e1.length_mm;
}
else {
// cast before the divide so that the sum is promoted
double frac = (double) e2.length_mm / (e1.length_mm + e2.length_mm);
e2.length_mm = (int) (length_mm * frac);
e1.length_mm = length_mm - e2.length_mm;
}

if (e1.length_mm < 0 || e2.length_mm < 0) {
e1.tov.removeIncoming(e1);
e1.fromv.removeOutgoing(e1);
e2.tov.removeIncoming(e2);
e2.fromv.removeOutgoing(e2);
throw new IllegalStateException("Split street is longer than original street!");
}
StreetEdge e1 = null;
StreetEdge e2 = null;

if (destructive) {
e1 = new StreetEdge((StreetVertex) fromv, v, geoms.first, name, 0, permission, this.isBack());
e2 = new StreetEdge(v, (StreetVertex) tov, geoms.second, name, 0, permission, this.isBack());

// figure the lengths, ensuring that they sum to the length of this edge
e1.calculateLengthFromGeometry();
e2.calculateLengthFromGeometry();

// we have this code implemented in both directions, because splits are fudged half a millimeter
// when the length of this is odd. We want to make sure the lengths of the split streets end up
// exactly the same as their backStreets so that if they are split again the error does not accumulate
// and so that the order in which they are split does not matter.
if (!isBack()) {
// cast before the divide so that the sum is promoted
double frac = (double) e1.length_mm / (e1.length_mm + e2.length_mm);
e1.length_mm = (int) (length_mm * frac);
e2.length_mm = length_mm - e1.length_mm;
}
else {
// cast before the divide so that the sum is promoted
double frac = (double) e2.length_mm / (e1.length_mm + e2.length_mm);
e2.length_mm = (int) (length_mm * frac);
e1.length_mm = length_mm - e2.length_mm;
}

if (e1.length_mm < 0 || e2.length_mm < 0) {
e1.tov.removeIncoming(e1);
e1.fromv.removeOutgoing(e1);
e2.tov.removeIncoming(e2);
e2.fromv.removeOutgoing(e2);
throw new IllegalStateException("Split street is longer than original street!");
}

for (StreetEdge e : new StreetEdge[] { e1, e2 }) {
e.setBicycleSafetyFactor(getBicycleSafetyFactor());
e.setHasBogusName(hasBogusName());
e.setStairs(isStairs());
e.setWheelchairAccessible(isWheelchairAccessible());
e.setBack(isBack());
for (StreetEdge e : new StreetEdge[] { e1, e2 }) {
e.setBicycleSafetyFactor(getBicycleSafetyFactor());
e.setHasBogusName(hasBogusName());
e.setStairs(isStairs());
e.setWheelchairAccessible(isWheelchairAccessible());
e.setBack(isBack());
}
} else {
if (((TemporarySplitterVertex) v).isEndVertex()) {
e1 = new TemporaryPartialStreetEdge(this, (StreetVertex) fromv, (TemporarySplitterVertex) v, geoms.first, name, 0);
e1.calculateLengthFromGeometry();
e1.setNoThruTraffic(this.isNoThruTraffic());
e1.setStreetClass(this.getStreetClass());
} else {
e2 = new TemporaryPartialStreetEdge(this, (TemporarySplitterVertex) v, (StreetVertex) tov, geoms.second, name, 0);
e2.calculateLengthFromGeometry();
e2.setNoThruTraffic(this.isNoThruTraffic());
e2.setStreetClass(this.getStreetClass());
}
}





return new P2<StreetEdge>(e1, e2);
}

Expand Down
Expand Up @@ -16,6 +16,8 @@ the License, or (at your option) any later version.
import com.vividsolutions.jts.geom.LineString;
import org.opentripplanner.routing.location.TemporaryStreetLocation;
import org.opentripplanner.routing.vertextype.StreetVertex;
import org.opentripplanner.routing.vertextype.TemporarySplitterVertex;
import org.opentripplanner.routing.vertextype.TemporaryVertex;
import org.opentripplanner.util.I18NString;

final public class TemporaryPartialStreetEdge extends PartialStreetEdge implements TemporaryEdge {
Expand Down Expand Up @@ -45,6 +47,17 @@ public TemporaryPartialStreetEdge(StreetEdge parentEdge, TemporaryStreetLocation
}
}

public TemporaryPartialStreetEdge(StreetEdge parentEdge, TemporarySplitterVertex v1,
StreetVertex v2, LineString geometry, I18NString name, double length) {
super(parentEdge, v1, v2, geometry, name, length);

if (v1.isEndVertex()) {
throw new IllegalStateException("A temporary edge is directed away from an end vertex");
} else {
endEdge = false;
}
}

public TemporaryPartialStreetEdge(StreetEdge parentEdge, StreetVertex v1,
TemporaryStreetLocation v2, LineString geometry, I18NString name, double length) {
super(parentEdge, v1, v2, geometry, name, length);
Expand All @@ -56,6 +69,17 @@ public TemporaryPartialStreetEdge(StreetEdge parentEdge, StreetVertex v1,
}
}

public TemporaryPartialStreetEdge(StreetEdge parentEdge, StreetVertex v1,
TemporarySplitterVertex v2, LineString geometry, I18NString name, double length) {
super(parentEdge, v1, v2, geometry, name, length);

if (v2.isEndVertex()) {
endEdge = true;
} else {
throw new IllegalStateException("A temporary edge is directed towards a start vertex");
}
}

@Override
public void dispose() {
if (endEdge != null) {
Expand Down

0 comments on commit 7480a51

Please sign in to comment.