use of org.locationtech.jts.linearref.LinearLocation in project OpenTripPlanner by opentripplanner.
the class GeometryAndBlockProcessor method getHopGeometryViaShapeDistTraveled.
private LineString getHopGeometryViaShapeDistTraveled(FeedScopedId shapeId, StopTime st0, StopTime st1) {
double startDistance = st0.getShapeDistTraveled();
double endDistance = st1.getShapeDistTraveled();
ShapeSegmentKey key = new ShapeSegmentKey(shapeId, startDistance, endDistance);
LineString geometry = geometriesByShapeSegmentKey.get(key);
if (geometry != null)
return geometry;
double[] distances = getDistanceForShapeId(shapeId);
if (distances == null) {
issueStore.add(new BogusShapeGeometry(shapeId));
return null;
} else {
LinearLocation startIndex = getSegmentFraction(distances, startDistance);
LinearLocation endIndex = getSegmentFraction(distances, endDistance);
if (equals(startIndex, endIndex)) {
// bogus shape_dist_traveled
issueStore.add(new BogusShapeDistanceTraveled(st1));
return createSimpleGeometry(st0.getStop(), st1.getStop());
}
LineString line = getLineStringForShapeId(shapeId);
LocationIndexedLine lol = new LocationIndexedLine(line);
geometry = getSegmentGeometry(shapeId, lol, startIndex, endIndex, startDistance, endDistance, st0, st1);
return geometry;
}
}
use of org.locationtech.jts.linearref.LinearLocation in project OpenTripPlanner by opentripplanner.
the class GeometryAndBlockProcessor method createGeometry.
/**
* Creates a set of geometries for a single trip, considering the GTFS shapes.txt,
* The geometry is broken down into one geometry per inter-stop segment ("hop"). We also need a shape for the entire
* trip and tripPattern, but given the complexity of the existing code for generating hop geometries, we will create
* the full-trip geometry by simply concatenating the hop geometries.
*
* This geometry will in fact be used for an entire set of trips in a trip pattern. Technically one of the trips
* with exactly the same sequence of stops could follow a different route on the streets, but that's very uncommon.
*/
private LineString[] createGeometry(FeedScopedId shapeId, List<StopTime> stopTimes) {
if (hasShapeDist(shapeId, stopTimes)) {
// this trip has shape_dist in stop_times
return getHopGeometriesViaShapeDistTravelled(stopTimes, shapeId);
}
LineString shapeLineString = getLineStringForShapeId(shapeId);
if (shapeLineString == null) {
// create straight line segments between stops for each hop
return createStraightLineHopeGeometries(stopTimes, shapeId);
}
List<LinearLocation> locations = getLinearLocations(stopTimes, shapeLineString);
if (locations == null) {
// linking, even though theoretically we could do better.
return createStraightLineHopeGeometries(stopTimes, shapeId);
}
return getGeometriesByShape(stopTimes, shapeId, shapeLineString, locations);
}
use of org.locationtech.jts.linearref.LinearLocation in project OpenTripPlanner by opentripplanner.
the class StreetMatcher method match.
@SuppressWarnings("unchecked")
public List<Edge> match(Geometry routeGeometry) {
routeGeometry = removeDuplicatePoints(routeGeometry);
if (routeGeometry == null)
return null;
routeGeometry = DouglasPeuckerSimplifier.simplify(routeGeometry, 0.00001);
// initial state: start midway along a block.
LocationIndexedLine indexedLine = new LocationIndexedLine(routeGeometry);
LinearLocation startIndex = indexedLine.getStartIndex();
Coordinate routeStartCoordinate = startIndex.getCoordinate(routeGeometry);
Envelope envelope = new Envelope(routeStartCoordinate);
double distanceThreshold = DISTANCE_THRESHOLD;
envelope.expandBy(distanceThreshold);
BinHeap<MatchState> states = new BinHeap<MatchState>();
List<Edge> nearbyEdges = index.query(envelope);
while (nearbyEdges.isEmpty()) {
envelope.expandBy(distanceThreshold);
distanceThreshold *= 2;
nearbyEdges = index.query(envelope);
}
// compute initial states
for (Edge initialEdge : nearbyEdges) {
Geometry edgeGeometry = initialEdge.getGeometry();
LocationIndexedLine indexedEdge = new LocationIndexedLine(edgeGeometry);
LinearLocation initialLocation = indexedEdge.project(routeStartCoordinate);
double error = MatchState.distance(initialLocation.getCoordinate(edgeGeometry), routeStartCoordinate);
MidblockMatchState state = new MidblockMatchState(null, routeGeometry, initialEdge, startIndex, initialLocation, error, 0.01);
// make sure all initial states are visited by inserting them at 0
states.insert(state, 0);
}
// search for best-matching path
int seen_count = 0, total = 0;
HashSet<MatchState> seen = new HashSet<MatchState>();
while (!states.empty()) {
double k = states.peek_min_key();
MatchState state = states.extract_min();
if (++total % 50000 == 0) {
log.debug("seen / total: " + seen_count + " / " + total);
}
if (seen.contains(state)) {
++seen_count;
continue;
} else {
if (k != 0) {
// but do not mark states as closed if we start at them
seen.add(state);
}
}
if (state instanceof EndMatchState) {
return toEdgeList(state);
}
for (MatchState next : state.getNextStates()) {
if (seen.contains(next)) {
continue;
}
states.insert(next, next.getTotalError() - next.getDistanceAlongRoute());
}
}
return null;
}
use of org.locationtech.jts.linearref.LinearLocation in project OpenTripPlanner by opentripplanner.
the class MidblockMatchState method getNextStates.
@Override
public List<MatchState> getNextStates() {
ArrayList<MatchState> nextStates = new ArrayList<MatchState>();
if (routeIndex.getSegmentIndex() == routeGeometry.getNumPoints() - 1) {
// this has either hit the end, or gone off the end. It's not real clear which.
// for now, let's assume it means that the ending is somewhere along this edge,
// so we return an end state
Coordinate pt = routeIndex.getCoordinate(routeGeometry);
double error = distance(pt, edgeIndex.getCoordinate(edgeGeometry));
nextStates.add(new EndMatchState(this, error, 0));
return nextStates;
}
LinearIterator it = new LinearIterator(routeGeometry, routeIndex);
if (it.hasNext()) {
it.next();
LinearLocation routeSuccessor = it.getLocation();
// now we want to see where this new point is in terms of the edge's geometry
Coordinate newRouteCoord = routeSuccessor.getCoordinate(routeGeometry);
LinearLocation newEdgeIndex = indexedEdge.project(newRouteCoord);
Coordinate edgeCoord = newEdgeIndex.getCoordinate(edgeGeometry);
if (newEdgeIndex.compareTo(edgeIndex) <= 0) {
/* this should not require the try/catch, but there is a bug in JTS */
try {
LinearLocation projected2 = indexedEdge.indexOfAfter(edgeCoord, edgeIndex);
// another bug in JTS
if (Double.isNaN(projected2.getSegmentFraction())) {
// we are probably moving backwards
return Collections.emptyList();
} else {
newEdgeIndex = projected2;
if (newEdgeIndex.equals(edgeIndex)) {
return Collections.emptyList();
}
}
edgeCoord = newEdgeIndex.getCoordinate(edgeGeometry);
} catch (AssertionFailedException e) {
// we are not making progress, so just return an empty list
return Collections.emptyList();
}
}
if (newEdgeIndex.getSegmentIndex() == edgeGeometry.getNumPoints() - 1) {
// we might choose to continue from the end of the edge and a point mid-way
// along this route segment
// find nearest point that makes progress along the route
Vertex toVertex = edge.getToVertex();
Coordinate endCoord = toVertex.getCoordinate();
LocationIndexedLine indexedRoute = new LocationIndexedLine(routeGeometry);
// FIXME: it would be better to do this project/indexOfAfter in one step
// as the two-step version could snap to a bad place and be unable to escape.
LinearLocation routeProjectedEndIndex = indexedRoute.project(endCoord);
Coordinate routeProjectedEndCoord = routeProjectedEndIndex.getCoordinate(routeGeometry);
if (routeProjectedEndIndex.compareTo(routeIndex) <= 0) {
try {
routeProjectedEndIndex = indexedRoute.indexOfAfter(routeProjectedEndCoord, routeIndex);
if (Double.isNaN(routeProjectedEndIndex.getSegmentFraction())) {
// can't go forward
// this is bad, but not terrible
routeProjectedEndIndex = routeIndex;
// since we are advancing along the edge
}
} catch (AssertionFailedException e) {
routeProjectedEndIndex = routeIndex;
}
routeProjectedEndCoord = routeProjectedEndIndex.getCoordinate(routeGeometry);
}
double positionError = distance(routeProjectedEndCoord, endCoord);
double travelAlongRoute = distanceAlongGeometry(routeGeometry, routeIndex, routeProjectedEndIndex);
double travelAlongEdge = distanceAlongGeometry(edgeGeometry, edgeIndex, newEdgeIndex);
double travelError = Math.abs(travelAlongEdge - travelAlongRoute);
double error = positionError + travelError;
if (error > MAX_ERROR) {
// totally wrong
return nextStates;
}
for (Edge e : getOutgoingMatchableEdges(toVertex)) {
double cost = error + NEW_SEGMENT_PENALTY;
if (!carsCanTraverse(e)) {
cost += NO_TRAVERSE_PENALTY;
}
MatchState nextState = new MidblockMatchState(this, routeGeometry, e, routeProjectedEndIndex, new LinearLocation(), cost, travelAlongRoute);
nextStates.add(nextState);
}
} else {
double travelAlongEdge = distanceAlongGeometry(edgeGeometry, edgeIndex, newEdgeIndex);
double travelAlongRoute = distanceAlongGeometry(routeGeometry, routeIndex, routeSuccessor);
double travelError = Math.abs(travelAlongRoute - travelAlongEdge);
double positionError = distance(edgeCoord, newRouteCoord);
double error = travelError + positionError;
MatchState nextState = new MidblockMatchState(this, routeGeometry, edge, routeSuccessor, newEdgeIndex, error, travelAlongRoute);
nextStates.add(nextState);
// it's also possible that, although we have not yet reached the end of this edge,
// we are going to turn, because the route turns earlier than the edge. In that
// case, we jump to the corner, and our error is the distance from the route point
// and the corner
Vertex toVertex = edge.getToVertex();
double travelAlongOldEdge = distanceAlongGeometry(edgeGeometry, edgeIndex, null);
for (Edge e : getOutgoingMatchableEdges(toVertex)) {
Geometry newEdgeGeometry = e.getGeometry();
LocationIndexedLine newIndexedEdge = new LocationIndexedLine(newEdgeGeometry);
newEdgeIndex = newIndexedEdge.project(newRouteCoord);
Coordinate newEdgeCoord = newEdgeIndex.getCoordinate(newEdgeGeometry);
positionError = distance(newEdgeCoord, newRouteCoord);
travelAlongEdge = travelAlongOldEdge + distanceAlongGeometry(newEdgeGeometry, new LinearLocation(), newEdgeIndex);
travelError = Math.abs(travelAlongRoute - travelAlongEdge);
error = travelError + positionError;
if (error > MAX_ERROR) {
// totally wrong
return nextStates;
}
double cost = error + NEW_SEGMENT_PENALTY;
if (!carsCanTraverse(e)) {
cost += NO_TRAVERSE_PENALTY;
}
nextState = new MidblockMatchState(this, routeGeometry, e, routeSuccessor, new LinearLocation(), cost, travelAlongRoute);
nextStates.add(nextState);
}
}
return nextStates;
} else {
Coordinate routeCoord = routeIndex.getCoordinate(routeGeometry);
LinearLocation projected = indexedEdge.project(routeCoord);
double locationError = distance(projected.getCoordinate(edgeGeometry), routeCoord);
MatchState end = new EndMatchState(this, locationError, 0);
return Arrays.asList(end);
}
}
use of org.locationtech.jts.linearref.LinearLocation in project OpenTripPlanner by opentripplanner.
the class GeometryAndBlockProcessor method getSegmentFraction.
private LinearLocation getSegmentFraction(double[] distances, double distance) {
int index = Arrays.binarySearch(distances, distance);
if (index < 0)
index = -(index + 1);
if (index == 0)
return new LinearLocation(0, 0.0);
if (index == distances.length)
return new LinearLocation(distances.length, 0.0);
double prevDistance = distances[index - 1];
if (prevDistance == distances[index]) {
return new LinearLocation(index - 1, 1.0);
}
double indexPart = (distance - distances[index - 1]) / (distances[index] - prevDistance);
return new LinearLocation(index - 1, indexPart);
}
Aggregations