Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.opentripplanner.graph_builder.module.osm;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.opentripplanner.common.geometry.GeometryUtils;
import org.opentripplanner.common.geometry.SphericalDistanceLibrary;
import org.opentripplanner.common.model.P2;
import org.opentripplanner.graph_builder.module.osm.OpenStreetMapModule.Handler;
import org.opentripplanner.graph_builder.services.StreetEdgeFactory;
import org.opentripplanner.openstreetmap.model.OSMNode;
import org.opentripplanner.openstreetmap.model.OSMWithTags;
import org.opentripplanner.routing.algorithm.GenericDijkstra;
import org.opentripplanner.routing.algorithm.strategies.SkipEdgeStrategy;
import org.opentripplanner.routing.core.RoutingRequest;
import org.opentripplanner.routing.core.State;
import org.opentripplanner.routing.core.TraverseMode;
import org.opentripplanner.routing.edgetype.AreaEdge;
import org.opentripplanner.routing.edgetype.AreaEdgeList;
import org.opentripplanner.routing.edgetype.NamedArea;
import org.opentripplanner.routing.edgetype.StreetEdge;
import org.opentripplanner.routing.edgetype.StreetTraversalPermission;
import org.opentripplanner.routing.graph.Edge;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.graph.Vertex;
import org.opentripplanner.routing.spt.DominanceFunction;
import org.opentripplanner.routing.spt.GraphPath;
import org.opentripplanner.routing.spt.ShortestPathTree;
import org.opentripplanner.routing.vertextype.IntersectionVertex;
import org.opentripplanner.visibility.Environment;
import org.opentripplanner.visibility.VLPoint;
import org.opentripplanner.visibility.VLPolygon;
import org.opentripplanner.visibility.VisibilityPolygon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.opentripplanner.util.I18NString;
/**
* Theoretically, it is not correct to build the visibility graph on the joined polygon of areas
* with different levels of bike safety. That's because in the optimal path, you might end up
* changing direction at area boundaries. The problem is known as "weighted planar subdivisions",
* and the best known algorithm is O(N^3). That's not much worse than general visibility graph
* construction, but it would have to be done at runtime to account for the differences in bike
* safety preferences. Ted Chiang's "Story Of Your Life" describes how a very similar problem in
* optics gives rise to Snell's Law. It is the second-best story about a law of physics that I know
* of (Chiang's "Exhalation" is the first).
*
* Anyway, since we're not going to run an O(N^3) algorithm at runtime just to give people who don't
* understand Snell's Law weird paths that they can complain about, this should be just fine.
*
*/
public class WalkableAreaBuilder {
private static Logger LOG = LoggerFactory.getLogger(WalkableAreaBuilder.class);
private final int MAX_AREA_NODES = 500;
private static final double VISIBILITY_EPSILON = 0.000000001;
private Graph graph;
private OSMDatabase osmdb;
private WayPropertySet wayPropertySet;
private StreetEdgeFactory edgeFactory;
// This is an awful hack, but this class (WalkableAreaBuilder) ought to be rewritten.
private Handler handler;
private HashMap areaBoundaryVertexForCoordinate = new HashMap();
public WalkableAreaBuilder(Graph graph, OSMDatabase osmdb, WayPropertySet wayPropertySet,
StreetEdgeFactory edgeFactory, Handler handler) {
this.graph = graph;
this.osmdb = osmdb;
this.wayPropertySet = wayPropertySet;
this.edgeFactory = edgeFactory;
this.handler = handler;
}
/**
* For all areas just use outermost rings as edges so that areas can be routable without visibility calculations
* @param group
*/
public void buildWithoutVisibility(AreaGroup group) {
Set edges = new HashSet();
// create polygon and accumulate nodes for area
for (Ring ring : group.outermostRings) {
AreaEdgeList edgeList = new AreaEdgeList();
// the points corresponding to concave or hole vertices
// or those linked to ways
HashSet> alreadyAddedEdges = new HashSet>();
// we also want to fill in the edges of this area anyway, because we can,
// and to avoid the numerical problems that they tend to cause
for (Area area : group.areas) {
if (!ring.toJtsPolygon().contains(area.toJTSMultiPolygon())) {
continue;
}
for (Ring outerRing : area.outermostRings) {
for (int i = 0; i < outerRing.nodes.size(); ++i) {
createEdgesForRingSegment(edges, edgeList, area, outerRing, i,
alreadyAddedEdges);
}
//TODO: is this actually needed?
for (Ring innerRing : outerRing.holes) {
for (int j = 0; j < innerRing.nodes.size(); ++j) {
createEdgesForRingSegment(edges, edgeList, area, innerRing, j,
alreadyAddedEdges);
}
}
}
}
}
}
public void buildWithVisibility(AreaGroup group, boolean platformEntriesLinking) {
Set startingNodes = new HashSet();
Set startingVertices = new HashSet();
Set edges = new HashSet();
// create polygon and accumulate nodes for area
for (Ring ring : group.outermostRings) {
AreaEdgeList edgeList = new AreaEdgeList();
// the points corresponding to concave or hole vertices
// or those linked to ways
ArrayList visibilityPoints = new ArrayList();
ArrayList visibilityNodes = new ArrayList();
HashSet> alreadyAddedEdges = new HashSet>();
// we need to accumulate visibility points from all contained areas
// inside this ring, but only for shared nodes; we don't care about
// convexity, which we'll handle for the grouped area only.
// we also want to fill in the edges of this area anyway, because we can,
// and to avoid the numerical problems that they tend to cause
for (Area area : group.areas) {
// public transform platforms will be handled separately if platformEntriesLinking
// parameter is true
if(platformEntriesLinking
&& "platform".equals(area.parent.getTag("public_transport"))) {
continue;
}
if (!ring.toJtsPolygon().contains(area.toJTSMultiPolygon())) {
continue;
}
// Add stops from public transit relations into the area
Collection nodes = osmdb.getStopsInArea(area.parent);
if (nodes != null) {
for (OSMNode node : nodes) {
addtoVisibilityAndStartSets(startingNodes, visibilityPoints,
visibilityNodes, node);
}
}
for (Ring outerRing : area.outermostRings) {
for (int i = 0; i < outerRing.nodes.size(); ++i) {
OSMNode node = outerRing.nodes.get(i);
createEdgesForRingSegment(edges, edgeList, area, outerRing, i,
alreadyAddedEdges);
addtoVisibilityAndStartSets(startingNodes, visibilityPoints,
visibilityNodes, node);
}
for (Ring innerRing : outerRing.holes) {
for (int j = 0; j < innerRing.nodes.size(); ++j) {
OSMNode node = innerRing.nodes.get(j);
createEdgesForRingSegment(edges, edgeList, area, innerRing, j,
alreadyAddedEdges);
addtoVisibilityAndStartSets(startingNodes, visibilityPoints,
visibilityNodes, node);
}
}
}
}
List nodes = new ArrayList();
List vertices = new ArrayList();
accumulateRingNodes(ring, nodes, vertices);
VLPolygon polygon = makeStandardizedVLPolygon(vertices, nodes, false);
accumulateVisibilityPoints(ring.nodes, polygon, visibilityPoints, visibilityNodes,
false);
ArrayList polygons = new ArrayList();
polygons.add(polygon);
// holes
for (Ring innerRing : ring.holes) {
ArrayList holeNodes = new ArrayList();
vertices = new ArrayList();
accumulateRingNodes(innerRing, holeNodes, vertices);
VLPolygon hole = makeStandardizedVLPolygon(vertices, holeNodes, true);
accumulateVisibilityPoints(innerRing.nodes, hole, visibilityPoints,
visibilityNodes, true);
nodes.addAll(holeNodes);
polygons.add(hole);
}
Environment areaEnv = new Environment(polygons);
// FIXME: temporary hard limit on size of
// areas to prevent way explosion
if (visibilityPoints.size() > MAX_AREA_NODES) {
LOG.warn("Area " + group.getSomeOSMObject() + " is too complicated ("
+ visibilityPoints.size() + " > " + MAX_AREA_NODES);
continue;
}
if (!areaEnv.is_valid(VISIBILITY_EPSILON)) {
LOG.warn("Area " + group.getSomeOSMObject() + " is not epsilon-valid (epsilon = "
+ VISIBILITY_EPSILON + ")");
continue;
}
edgeList.setOriginalEdges(ring.toJtsPolygon());
createNamedAreas(edgeList, ring, group.areas);
OSMWithTags areaEntity = group.getSomeOSMObject();
for (int i = 0; i < visibilityNodes.size(); ++i) {
OSMNode nodeI = visibilityNodes.get(i);
VisibilityPolygon visibilityPolygon = new VisibilityPolygon(
visibilityPoints.get(i), areaEnv, VISIBILITY_EPSILON);
Polygon poly = toJTSPolygon(visibilityPolygon);
for (int j = 0; j < visibilityNodes.size(); ++j) {
OSMNode nodeJ = visibilityNodes.get(j);
P2 nodePair = new P2(nodeI, nodeJ);
if (alreadyAddedEdges.contains(nodePair))
continue;
IntersectionVertex startEndpoint = handler.getVertexForOsmNode(nodeI,
areaEntity);
IntersectionVertex endEndpoint = handler.getVertexForOsmNode(nodeJ,
areaEntity);
Coordinate[] coordinates = new Coordinate[] { startEndpoint.getCoordinate(),
endEndpoint.getCoordinate() };
GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory();
LineString line = geometryFactory.createLineString(coordinates);
if (poly != null && poly.contains(line)) {
createSegments(nodeI, nodeJ, startEndpoint, endEndpoint, group.areas,
edgeList, edges);
if (startingNodes.contains(nodeI)) {
startingVertices.add(startEndpoint);
}
if (startingNodes.contains(nodeJ)) {
startingVertices.add(endEndpoint);
}
}
}
}
}
pruneAreaEdges(startingVertices, edges);
}
class ListedEdgesOnly implements SkipEdgeStrategy {
private Set edges;
public ListedEdgesOnly(Set edges) {
this.edges = edges;
}
@Override
public boolean shouldSkipEdge(Vertex origin, Vertex target, State current, Edge edge,
ShortestPathTree spt, RoutingRequest traverseOptions) {
return !edges.contains(edge);
}
}
/**
* Do an all-pairs shortest path search from a list of vertices over a specified set of edges,
* and retain only those edges which are actually used in some shortest path.
*
* @param startingVertices
* @param edges
*/
private void pruneAreaEdges(Collection startingVertices, Set edges) {
if (edges.size() == 0)
return;
TraverseMode mode;
StreetEdge firstEdge = (StreetEdge) edges.iterator().next();
if (firstEdge.getPermission().allows(StreetTraversalPermission.PEDESTRIAN)) {
mode = TraverseMode.WALK;
} else if (firstEdge.getPermission().allows(StreetTraversalPermission.BICYCLE)) {
mode = TraverseMode.BICYCLE;
} else {
mode = TraverseMode.CAR;
}
RoutingRequest options = new RoutingRequest(mode);
options.setDummyRoutingContext(graph);
options.dominanceFunction = new DominanceFunction.EarliestArrival();
GenericDijkstra search = new GenericDijkstra(options);
search.setSkipEdgeStrategy(new ListedEdgesOnly(edges));
Set usedEdges = new HashSet();
for (Vertex vertex : startingVertices) {
State state = new State(vertex, options);
ShortestPathTree spt = search.getShortestPathTree(state);
for (Vertex endVertex : startingVertices) {
GraphPath path = spt.getPath(endVertex, false);
if (path != null) {
for (Edge edge : path.edges) {
usedEdges.add(edge);
}
}
}
}
for (Edge edge : edges) {
if (!usedEdges.contains(edge)) {
graph.removeEdge(edge);
}
}
}
private void addtoVisibilityAndStartSets(Set startingNodes,
ArrayList visibilityPoints, ArrayList visibilityNodes, OSMNode node) {
if (osmdb.isNodeBelongsToWay(node.getId())
|| osmdb.isNodeSharedByMultipleAreas(node.getId()) || node.isStop()) {
startingNodes.add(node);
VLPoint point = new VLPoint(node.lon, node.lat);
if (!visibilityPoints.contains(point)) {
visibilityPoints.add(point);
visibilityNodes.add(node);
}
}
}
private Polygon toJTSPolygon(VLPolygon visibilityPolygon) {
if (visibilityPolygon.vertices.isEmpty()) {
return null;
}
// incomprehensibly, visilibity's routines for figuring out point-polygon containment are
// too broken
// to use here, so we have to fall back to JTS.
Coordinate[] coordinates = new Coordinate[visibilityPolygon.n() + 1];
for (int p = 0; p < coordinates.length; ++p) {
VLPoint vlPoint = visibilityPolygon.get(p);
coordinates[p] = new Coordinate(vlPoint.x, vlPoint.y);
}
LinearRing shell = GeometryUtils.getGeometryFactory().createLinearRing(coordinates);
Polygon poly = GeometryUtils.getGeometryFactory().createPolygon(shell, new LinearRing[0]);
return poly;
}
private void createEdgesForRingSegment(Set edges, AreaEdgeList edgeList, Area area,
Ring ring, int i, HashSet> alreadyAddedEdges) {
OSMNode node = ring.nodes.get(i);
OSMNode nextNode = ring.nodes.get((i + 1) % ring.nodes.size());
P2 nodePair = new P2(node, nextNode);
if (alreadyAddedEdges.contains(nodePair)) {
return;
}
alreadyAddedEdges.add(nodePair);
IntersectionVertex startEndpoint = handler.getVertexForOsmNode(node, area.parent);
IntersectionVertex endEndpoint = handler.getVertexForOsmNode(nextNode, area.parent);
createSegments(node, nextNode, startEndpoint, endEndpoint, Arrays.asList(area), edgeList,
edges);
}
private void createSegments(OSMNode fromNode, OSMNode toNode, IntersectionVertex startEndpoint,
IntersectionVertex endEndpoint, Collection areas, AreaEdgeList edgeList,
Set edges) {
List intersects = new ArrayList();
Coordinate[] coordinates = new Coordinate[] { startEndpoint.getCoordinate(),
endEndpoint.getCoordinate() };
GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory();
LineString line = geometryFactory.createLineString(coordinates);
for (Area area : areas) {
MultiPolygon polygon = area.toJTSMultiPolygon();
Geometry intersection = polygon.intersection(line);
if (intersection.getLength() > 0.000001) {
intersects.add(area);
}
}
if (intersects.size() == 0) {
// apparently our intersection here was bogus
return;
}
// do we need to recurse?
if (intersects.size() == 1) {
Area area = intersects.get(0);
OSMWithTags areaEntity = area.parent;
StreetTraversalPermission areaPermissions = OSMFilter.getPermissionsForEntity(
areaEntity, StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE);
float carSpeed = wayPropertySet.getCarSpeedForWay(areaEntity, false);
double length = SphericalDistanceLibrary.distance(startEndpoint.getCoordinate(),
endEndpoint.getCoordinate());
int cls = StreetEdge.CLASS_OTHERPATH;
cls |= OSMFilter.getStreetClasses(areaEntity);
String label = "way (area) " + areaEntity.getId() + " from " + startEndpoint.getLabel()
+ " to " + endEndpoint.getLabel();
I18NString name = handler.getNameForWay(areaEntity, label);
AreaEdge street = edgeFactory.createAreaEdge(startEndpoint, endEndpoint, line, name,
length, areaPermissions, false, edgeList);
street.setCarSpeed(carSpeed);
if (!areaEntity.hasTag("name") && !areaEntity.hasTag("ref")) {
street.setHasBogusName(true);
}
if (areaEntity.isTagFalse("wheelchair")) {
street.setWheelchairAccessible(false);
}
street.setStreetClass(cls);
edges.add(street);
label = "way (area) " + areaEntity.getId() + " from " + endEndpoint.getLabel() + " to "
+ startEndpoint.getLabel();
name = handler.getNameForWay(areaEntity, label);
AreaEdge backStreet = edgeFactory.createAreaEdge(endEndpoint, startEndpoint,
(LineString) line.reverse(), name, length, areaPermissions, true, edgeList);
backStreet.setCarSpeed(carSpeed);
if (!areaEntity.hasTag("name") && !areaEntity.hasTag("ref")) {
backStreet.setHasBogusName(true);
}
if (areaEntity.isTagFalse("wheelchair")) {
backStreet.setWheelchairAccessible(false);
}
backStreet.setStreetClass(cls);
edges.add(backStreet);
WayProperties wayData = wayPropertySet.getDataForWay(areaEntity);
handler.applyWayProperties(street, backStreet, wayData, areaEntity);
} else {
// take the part that intersects with the start vertex
Coordinate startCoordinate = startEndpoint.getCoordinate();
Point startPoint = geometryFactory.createPoint(startCoordinate);
for (Area area : intersects) {
MultiPolygon polygon = area.toJTSMultiPolygon();
if (!(polygon.intersects(startPoint) || polygon.getBoundary()
.intersects(startPoint)))
continue;
Geometry lineParts = line.intersection(polygon);
if (lineParts.getLength() > 0.000001) {
Coordinate edgeCoordinate = null;
// this is either a LineString or a MultiLineString (we hope)
if (lineParts instanceof MultiLineString) {
MultiLineString mls = (MultiLineString) lineParts;
boolean found = false;
for (int i = 0; i < mls.getNumGeometries(); ++i) {
LineString segment = (LineString) mls.getGeometryN(i);
if (found) {
edgeCoordinate = segment.getEndPoint().getCoordinate();
break;
}
if (segment.contains(startPoint)
|| segment.getBoundary().contains(startPoint)) {
found = true;
if (segment.getLength() > 0.000001) {
edgeCoordinate = segment.getEndPoint().getCoordinate();
break;
}
}
}
} else if (lineParts instanceof LineString) {
edgeCoordinate = ((LineString) lineParts).getEndPoint().getCoordinate();
} else {
continue;
}
IntersectionVertex newEndpoint = areaBoundaryVertexForCoordinate
.get(edgeCoordinate);
if (newEndpoint == null) {
newEndpoint = new IntersectionVertex(graph, "area splitter at "
+ edgeCoordinate, edgeCoordinate.x, edgeCoordinate.y);
areaBoundaryVertexForCoordinate.put(edgeCoordinate, newEndpoint);
}
createSegments(fromNode, toNode, startEndpoint, newEndpoint,
Arrays.asList(area), edgeList, edges);
createSegments(fromNode, toNode, newEndpoint, endEndpoint, intersects,
edgeList, edges);
break;
}
}
}
}
private void createNamedAreas(AreaEdgeList edgeList, Ring ring, Collection areas) {
Polygon containingArea = ring.toJtsPolygon();
for (Area area : areas) {
Geometry intersection = containingArea.intersection(area.toJTSMultiPolygon());
if (intersection.getArea() == 0) {
continue;
}
NamedArea namedArea = new NamedArea();
OSMWithTags areaEntity = area.parent;
int cls = StreetEdge.CLASS_OTHERPATH;
cls |= OSMFilter.getStreetClasses(areaEntity);
namedArea.setStreetClass(cls);
String id = "way (area) " + areaEntity.getId() + " (splitter linking)";
I18NString name = handler.getNameForWay(areaEntity, id);
namedArea.setName(name);
WayProperties wayData = wayPropertySet.getDataForWay(areaEntity);
Double safety = wayData.getSafetyFeatures().first;
namedArea.setBicycleSafetyMultiplier(safety);
namedArea.setOriginalEdges(intersection);
StreetTraversalPermission permission = OSMFilter.getPermissionsForEntity(areaEntity,
StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE);
namedArea.setPermission(permission);
edgeList.addArea(namedArea);
}
}
private void accumulateRingNodes(Ring ring, List nodes, List vertices) {
for (OSMNode node : ring.nodes) {
if (nodes.contains(node)) {
// hopefully, this only happens in order to
// close polygons
continue;
}
VLPoint point = new VLPoint(node.lon, node.lat);
nodes.add(node);
vertices.add(point);
}
}
private void accumulateVisibilityPoints(List nodes, VLPolygon polygon,
List visibilityPoints, List visibilityNodes, boolean hole) {
int n = polygon.vertices.size();
for (int i = 0; i < n; ++i) {
OSMNode curNode = nodes.get(i);
VLPoint cur = polygon.vertices.get(i);
VLPoint prev = polygon.vertices.get((i + n - 1) % n);
VLPoint next = polygon.vertices.get((i + 1) % n);
if (hole
|| (cur.x - prev.x) * (next.y - cur.y) - (cur.y - prev.y) * (next.x - cur.x) > 0) {
// that math up there is a cross product to check
// if the point is concave. Note that the sign is reversed because
// visilibity is either ccw or latitude-major
if (!visibilityNodes.contains(curNode)) {
visibilityPoints.add(cur);
visibilityNodes.add(curNode);
}
}
}
}
private VLPolygon makeStandardizedVLPolygon(List vertices, List nodes,
boolean reversed) {
VLPolygon polygon = new VLPolygon(vertices);
if ((reversed && polygon.area() > 0) || (!reversed && polygon.area() < 0)) {
polygon.reverse();
// need to reverse nodes as well
reversePolygonOfOSMNodes(nodes);
}
if (!polygon.is_in_standard_form()) {
standardize(polygon.vertices, nodes);
}
return polygon;
}
private void standardize(ArrayList vertices, List nodes) {
// based on code from VisiLibity
int point_count = vertices.size();
if (point_count > 1) { // if more than one point in the polygon.
ArrayList vertices_temp = new ArrayList(point_count);
ArrayList nodes_temp = new ArrayList(point_count);
// Find index of lexicographically smallest point.
int index_of_smallest = 0;
for (int i = 1; i < point_count; i++)
if (vertices.get(i).compareTo(vertices.get(index_of_smallest)) < 0)
index_of_smallest = i;
// minor optimization for already-standardized polygons
if (index_of_smallest == 0)
return;
// Fill vertices_temp starting with lex. smallest.
for (int i = index_of_smallest; i < point_count; i++) {
vertices_temp.add(vertices.get(i));
nodes_temp.add(nodes.get(i));
}
for (int i = 0; i < index_of_smallest; i++) {
vertices_temp.add(vertices.get(i));
nodes_temp.add(nodes.get(i));
}
for (int i = 0; i < point_count; ++i) {
vertices.set(i, vertices_temp.get(i));
nodes.set(i, nodes_temp.get(i));
}
}
}
private void reversePolygonOfOSMNodes(List nodes) {
for (int i = 1; i < (nodes.size() + 1) / 2; ++i) {
OSMNode tmp = nodes.get(i);
int opposite = nodes.size() - i;
nodes.set(i, nodes.get(opposite));
nodes.set(opposite, tmp);
}
}
}