Skip to content

Commit

Permalink
use simplestreetsplitter to link bike rental, bike parking, and P+R.
Browse files Browse the repository at this point in the history
  • Loading branch information
mattwigway committed May 8, 2015
1 parent fce9811 commit d5d566a
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 232 deletions.
Expand Up @@ -19,18 +19,26 @@
import org.opentripplanner.common.geometry.HashGridSpatialIndex;
import org.opentripplanner.common.geometry.SphericalDistanceLibrary;
import org.opentripplanner.common.model.P2;
import org.opentripplanner.routing.bike_rental.BikeRentalStation;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.core.TraverseModeSet;
import org.opentripplanner.routing.edgetype.ParkAndRideLinkEdge;
import org.opentripplanner.routing.edgetype.PartialStreetEdge;
import org.opentripplanner.routing.edgetype.StreetBikeParkLink;
import org.opentripplanner.routing.edgetype.StreetBikeRentalLink;
import org.opentripplanner.routing.edgetype.StreetEdge;
import org.opentripplanner.routing.edgetype.StreetTransitLink;
import org.opentripplanner.routing.edgetype.StreetWithElevationEdge;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.vertextype.BikeParkVertex;
import org.opentripplanner.routing.vertextype.BikeRentalStationVertex;
import org.opentripplanner.routing.vertextype.ParkAndRideVertex;
import org.opentripplanner.routing.vertextype.SplitterVertex;
import org.opentripplanner.routing.vertextype.StreetVertex;
import org.opentripplanner.routing.vertextype.TransitStop;
import org.opentripplanner.updater.bike_park.BikeParkUpdater;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
Expand All @@ -54,97 +62,108 @@ public class SimpleStreetSplitter {

private static GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory();

/**
* Construct a new SimpleStreetSplitter. Be aware that only one SimpleStreetSplitter should be
* active on a graph at any given time.
* @param graph
*/
public SimpleStreetSplitter (Graph graph) {
this.graph = graph;
}

public void link () {

// build a nice private spatial index, since we're adding and removing edges
idx = new HashGridSpatialIndex<StreetEdge>();

for (StreetEdge se : Iterables.filter(graph.getEdges(), StreetEdge.class)) {
idx.insert(se.getGeometry().getEnvelopeInternal(), se);
}
}

public void link () {
for (Vertex v : graph.getVertices()) {
if (v instanceof TransitStop || v instanceof BikeRentalStationVertex || v instanceof ParkAndRideVertex)
link(v);
}
}

/** Link this vertex into the graph */
public void link (Vertex vertex) {
// 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
// bad and the gains in simplicity are considerable.
final double radiusDeg = SphericalDistanceLibrary.metersToDegrees(MAX_SEARCH_RADIUS_METERS);

STOPS: for (final TransitStop tstop : Iterables.filter(graph.getVertices(), TransitStop.class)) {
// 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
// bad and the gains in simplicity are considerable.
final double radiusDeg = SphericalDistanceLibrary.metersToDegrees(MAX_SEARCH_RADIUS_METERS);

Envelope env = new Envelope(tstop.getCoordinate());

// local equirectangular projection
final double xscale = Math.cos(tstop.getLat() * Math.PI / 180);

env.expandBy(radiusDeg / xscale, radiusDeg);

double duplicateDeg = SphericalDistanceLibrary.metersToDegrees(DUPLICATE_WAY_EPSILON_METERS);

// We sort the list of candidate edges by distance to the stop
// This should remove any issues with things coming out of the spatial index in different orders
// Then we link to everything that is within DUPLICATE_WAY_EPSILON_METERS of of the best distance
// so that we capture back edges and duplicate ways.
List<StreetEdge> candidateEdges = new ArrayList<StreetEdge>(
Collections2.filter(idx.query(env), new Predicate<StreetEdge>() {
Envelope env = new Envelope(vertex.getCoordinate());

// local equirectangular projection
final double xscale = Math.cos(vertex.getLat() * Math.PI / 180);

env.expandBy(radiusDeg / xscale, radiusDeg);

double duplicateDeg = SphericalDistanceLibrary.metersToDegrees(DUPLICATE_WAY_EPSILON_METERS);

// We sort the list of candidate edges by distance to the stop
// This should remove any issues with things coming out of the spatial index in different orders
// Then we link to everything that is within DUPLICATE_WAY_EPSILON_METERS of of the best distance
// so that we capture back edges and duplicate ways.
List<StreetEdge> candidateEdges = new ArrayList<StreetEdge>(
Collections2.filter(idx.query(env), new Predicate<StreetEdge>() {

@Override
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)) &&
// only link to edges still in the graph.
edge.getToVertex().getIncoming().contains(edge);
}
})
);
// make a map of distances
final TIntDoubleMap distances = new TIntDoubleHashMap();
for (StreetEdge e : candidateEdges) {
distances.put(e.getId(), distance(tstop, e, xscale));
}
// sort the list
Collections.sort(candidateEdges, new Comparator<StreetEdge> () {
@Override
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)) &&
// only link to edges still in the graph.
edge.getToVertex().getIncoming().contains(edge);
}
})
);

// make a map of distances
final TIntDoubleMap distances = new TIntDoubleHashMap();

for (StreetEdge e : candidateEdges) {
distances.put(e.getId(), distance(vertex, e, xscale));
}

// sort the list
Collections.sort(candidateEdges, new Comparator<StreetEdge> () {

@Override
public int compare(StreetEdge o1, StreetEdge o2) {
double diff = distances.get(o1.getId()) - distances.get(o2.getId());
if (diff < 0)
return -1;
if (diff > 0)
return 1;
return 0;
}

});

// find the closest candidate edges
if (candidateEdges.isEmpty() || distances.get(candidateEdges.get(0).getId()) > radiusDeg)
continue STOPS;

// find the best edge
double bestDist = distances.get(candidateEdges.get(0).getId());
List<StreetEdge> bestEdges = Lists.newArrayList();

// add edges until there is a break of epsilon meters.
int i = 0;
do {
bestEdges.add(candidateEdges.get(i++));
} while (i < candidateEdges.size() &&
distances.get(candidateEdges.get(i).getId()) - distances.get(candidateEdges.get(i - 1).getId()) < duplicateDeg);
@Override
public int compare(StreetEdge o1, StreetEdge o2) {
double diff = distances.get(o1.getId()) - distances.get(o2.getId());
if (diff < 0)
return -1;
if (diff > 0)
return 1;
return 0;
}

for (StreetEdge edge : bestEdges) {
link(tstop, edge, xscale);
}
}
});

// find the closest candidate edges
if (candidateEdges.isEmpty() || distances.get(candidateEdges.get(0).getId()) > radiusDeg)
return;

// find the best edge
double bestDist = distances.get(candidateEdges.get(0).getId());
List<StreetEdge> bestEdges = Lists.newArrayList();

// add edges until there is a break of epsilon meters.
int i = 0;
do {
bestEdges.add(candidateEdges.get(i++));
} while (i < candidateEdges.size() &&
distances.get(candidateEdges.get(i).getId()) - distances.get(candidateEdges.get(i - 1).getId()) < duplicateDeg);

for (StreetEdge edge : bestEdges) {
link(vertex, edge, xscale);
}
}

/** split the edge and link in the transit stop */
private void link (TransitStop tstop, StreetEdge edge, double xscale) {
private void link (Vertex tstop, StreetEdge edge, double xscale) {
// TODO: we've already built this line string, we should save it
LineString orig = edge.getGeometry();
LineString transformed = equirectangularProject(orig, xscale);
Expand Down Expand Up @@ -202,10 +221,42 @@ private SplitterVertex split (StreetEdge edge, LinearLocation ll) {
return v;
}

private void makeLinkEdges (Vertex from, StreetVertex to) {
if (from instanceof TransitStop)
makeTransitLinkEdges((TransitStop) from, to);
else if (from instanceof BikeRentalStationVertex)
makeBikeRentalLinkEdges((BikeRentalStationVertex) from, to);
else if (from instanceof BikeParkVertex)
makeBikeParkEdges((BikeParkVertex) from, to);
else if (from instanceof ParkAndRideVertex)
makeParkAndRideEdges((ParkAndRideVertex) from, to);
}

private void makeParkAndRideEdges(ParkAndRideVertex from, StreetVertex to) {
for (ParkAndRideLinkEdge prle : Iterables.filter(from.getOutgoing(), ParkAndRideLinkEdge.class)) {
if (prle.getToVertex() == to)
return;
}

new ParkAndRideLinkEdge(from, to);
new ParkAndRideLinkEdge(to, from);
}

/** Make bike park edges */
private void makeBikeParkEdges(BikeParkVertex from, StreetVertex to) {
for (StreetBikeParkLink sbpl : Iterables.filter(from.getOutgoing(), StreetBikeParkLink.class)) {
if (sbpl.getToVertex() == to)
return;
}

new StreetBikeParkLink(from, to);
new StreetBikeParkLink(to, from);
}

/**
* Make link edges, unless they already exist.
*/
private void makeLinkEdges (TransitStop tstop, StreetVertex v) {
private void makeTransitLinkEdges (TransitStop tstop, StreetVertex v) {
// ensure that the requisite edges do not already exist
// this can happen if we link to duplicate ways that have the same start/end vertices.
for (StreetTransitLink e : Iterables.filter(tstop.getOutgoing(), StreetTransitLink.class)) {
Expand All @@ -217,8 +268,19 @@ private void makeLinkEdges (TransitStop tstop, StreetVertex v) {
new StreetTransitLink(v, tstop, tstop.hasWheelchairEntrance());
}

/** Make link edges for bike rental */
private void makeBikeRentalLinkEdges (BikeRentalStationVertex from, StreetVertex to) {
for (StreetBikeRentalLink sbrl : Iterables.filter(from.getOutgoing(), StreetBikeRentalLink.class)) {
if (sbrl.getToVertex() == to)
return;
}

new StreetBikeRentalLink(from, to);
new StreetBikeRentalLink(to, from);
}

/** projected distance from stop to edge, in latitude degrees */
private static double distance (TransitStop tstop, StreetEdge edge, double xscale) {
private static double distance (Vertex tstop, StreetEdge edge, double xscale) {
// use JTS internal tools wherever possible
LineString transformed = equirectangularProject(edge.getGeometry(), xscale);
return transformed.distance(geometryFactory.createPoint(new Coordinate(tstop.getLon() * xscale, tstop.getLat())));
Expand Down

0 comments on commit d5d566a

Please sign in to comment.