Search in sources :

Example 1 with LocationIndexedLine

use of com.vividsolutions.jts.linearref.LocationIndexedLine 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;
}
Also used : LocationIndexedLine(com.vividsolutions.jts.linearref.LocationIndexedLine) LinearLocation(com.vividsolutions.jts.linearref.LinearLocation) BinHeap(org.opentripplanner.common.pqueue.BinHeap) Envelope(com.vividsolutions.jts.geom.Envelope) Geometry(com.vividsolutions.jts.geom.Geometry) Coordinate(com.vividsolutions.jts.geom.Coordinate) StreetEdge(org.opentripplanner.routing.edgetype.StreetEdge) Edge(org.opentripplanner.routing.graph.Edge) HashSet(java.util.HashSet)

Example 2 with LocationIndexedLine

use of com.vividsolutions.jts.linearref.LocationIndexedLine in project OpenTripPlanner by opentripplanner.

the class GeometryUtils method splitGeometryAtPoint.

/**
 * Splits the input geometry into two LineStrings at the given point.
 */
public static P2<LineString> splitGeometryAtPoint(Geometry geometry, Coordinate nearestPoint) {
    // An index in JTS can actually refer to any point along the line. It is NOT an array index.
    LocationIndexedLine line = new LocationIndexedLine(geometry);
    LinearLocation l = line.indexOf(nearestPoint);
    LineString beginning = (LineString) line.extractLine(line.getStartIndex(), l);
    LineString ending = (LineString) line.extractLine(l, line.getEndIndex());
    return new P2<LineString>(beginning, ending);
}
Also used : P2(org.opentripplanner.common.model.P2) LocationIndexedLine(com.vividsolutions.jts.linearref.LocationIndexedLine) LinearLocation(com.vividsolutions.jts.linearref.LinearLocation)

Example 3 with LocationIndexedLine

use of com.vividsolutions.jts.linearref.LocationIndexedLine 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);
    }
}
Also used : Vertex(org.opentripplanner.routing.graph.Vertex) LinearLocation(com.vividsolutions.jts.linearref.LinearLocation) LocationIndexedLine(com.vividsolutions.jts.linearref.LocationIndexedLine) ArrayList(java.util.ArrayList) Geometry(com.vividsolutions.jts.geom.Geometry) Coordinate(com.vividsolutions.jts.geom.Coordinate) AssertionFailedException(com.vividsolutions.jts.util.AssertionFailedException) Edge(org.opentripplanner.routing.graph.Edge)

Example 4 with LocationIndexedLine

use of com.vividsolutions.jts.linearref.LocationIndexedLine in project OpenTripPlanner by opentripplanner.

the class GeometryUtils method splitGeometryAtFraction.

/**
 * Splits the input geometry into two LineStrings at a fraction of the distance covered.
 */
public static P2<LineString> splitGeometryAtFraction(Geometry geometry, double fraction) {
    LineString empty = new LineString(null, gf);
    Coordinate[] coordinates = geometry.getCoordinates();
    CoordinateSequence sequence = gf.getCoordinateSequenceFactory().create(coordinates);
    LineString total = new LineString(sequence, gf);
    if (coordinates.length < 2)
        return new P2<LineString>(empty, empty);
    if (fraction <= 0)
        return new P2<LineString>(empty, total);
    if (fraction >= 1)
        return new P2<LineString>(total, empty);
    double totalDistance = total.getLength();
    double requestedDistance = totalDistance * fraction;
    // An index in JTS can actually refer to any point along the line. It is NOT an array index.
    LocationIndexedLine line = new LocationIndexedLine(geometry);
    LinearLocation l = LengthLocationMap.getLocation(geometry, requestedDistance);
    LineString beginning = (LineString) line.extractLine(line.getStartIndex(), l);
    LineString ending = (LineString) line.extractLine(l, line.getEndIndex());
    return new P2<LineString>(beginning, ending);
}
Also used : P2(org.opentripplanner.common.model.P2) LocationIndexedLine(com.vividsolutions.jts.linearref.LocationIndexedLine) LinearLocation(com.vividsolutions.jts.linearref.LinearLocation)

Example 5 with LocationIndexedLine

use of com.vividsolutions.jts.linearref.LocationIndexedLine in project OpenTripPlanner by opentripplanner.

the class GTFSPatternHopFactory 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(Graph graph, Trip trip, List<StopTime> stopTimes) {
    AgencyAndId shapeId = trip.getShapeId();
    // One less geometry than stoptime as array indexes represetn hops not stops (fencepost problem).
    LineString[] geoms = new LineString[stopTimes.size() - 1];
    // Detect presence or absence of shape_dist_traveled on a per-trip basis
    StopTime st0 = stopTimes.get(0);
    boolean hasShapeDist = st0.isShapeDistTraveledSet();
    if (hasShapeDist) {
        // this trip has shape_dist in stop_times
        for (int i = 0; i < stopTimes.size() - 1; ++i) {
            st0 = stopTimes.get(i);
            StopTime st1 = stopTimes.get(i + 1);
            geoms[i] = getHopGeometryViaShapeDistTraveled(graph, shapeId, st0, st1);
        }
        return geoms;
    }
    LineString shape = getLineStringForShapeId(shapeId);
    if (shape == null) {
        // create straight line segments between stops for each hop
        for (int i = 0; i < stopTimes.size() - 1; ++i) {
            st0 = stopTimes.get(i);
            StopTime st1 = stopTimes.get(i + 1);
            LineString geometry = createSimpleGeometry(st0.getStop(), st1.getStop());
            geoms[i] = geometry;
        }
        return geoms;
    }
    // This trip does not have shape_dist in stop_times, but does have an associated shape.
    ArrayList<IndexedLineSegment> segments = new ArrayList<IndexedLineSegment>();
    for (int i = 0; i < shape.getNumPoints() - 1; ++i) {
        segments.add(new IndexedLineSegment(i, shape.getCoordinateN(i), shape.getCoordinateN(i + 1)));
    }
    // Find possible segment matches for each stop.
    List<List<IndexedLineSegment>> possibleSegmentsForStop = new ArrayList<List<IndexedLineSegment>>();
    int minSegmentIndex = 0;
    for (int i = 0; i < stopTimes.size(); ++i) {
        Stop stop = stopTimes.get(i).getStop();
        Coordinate coord = new Coordinate(stop.getLon(), stop.getLat());
        List<IndexedLineSegment> stopSegments = new ArrayList<IndexedLineSegment>();
        double bestDistance = Double.MAX_VALUE;
        IndexedLineSegment bestSegment = null;
        int maxSegmentIndex = -1;
        int index = -1;
        int minSegmentIndexForThisStop = -1;
        for (IndexedLineSegment segment : segments) {
            index++;
            if (segment.index < minSegmentIndex) {
                continue;
            }
            double distance = segment.distance(coord);
            if (distance < maxStopToShapeSnapDistance) {
                stopSegments.add(segment);
                maxSegmentIndex = index;
                if (minSegmentIndexForThisStop == -1)
                    minSegmentIndexForThisStop = index;
            } else if (distance < bestDistance) {
                bestDistance = distance;
                bestSegment = segment;
                if (maxSegmentIndex != -1) {
                    maxSegmentIndex = index;
                }
            }
        }
        if (stopSegments.size() == 0) {
            // no segments within 150m
            // fall back to nearest segment
            stopSegments.add(bestSegment);
            minSegmentIndex = bestSegment.index;
        } else {
            minSegmentIndex = minSegmentIndexForThisStop;
            Collections.sort(stopSegments, new IndexedLineSegmentComparator(coord));
        }
        for (int j = i - 1; j >= 0; j--) {
            for (Iterator<IndexedLineSegment> it = possibleSegmentsForStop.get(j).iterator(); it.hasNext(); ) {
                IndexedLineSegment segment = it.next();
                if (segment.index > maxSegmentIndex) {
                    it.remove();
                }
            }
        }
        possibleSegmentsForStop.add(stopSegments);
    }
    List<LinearLocation> locations = getStopLocations(possibleSegmentsForStop, stopTimes, 0, -1);
    if (locations == null) {
        for (int i = 0; i < stopTimes.size() - 1; ++i) {
            st0 = stopTimes.get(i);
            StopTime st1 = stopTimes.get(i + 1);
            LineString geometry = createSimpleGeometry(st0.getStop(), st1.getStop());
            geoms[i] = geometry;
            // this warning is not strictly correct, but will do
            LOG.warn(graph.addBuilderAnnotation(new BogusShapeGeometryCaught(shapeId, st0, st1)));
        }
        return geoms;
    }
    Iterator<LinearLocation> locationIt = locations.iterator();
    LinearLocation endLocation = locationIt.next();
    double distanceSoFar = 0;
    int last = 0;
    for (int i = 0; i < stopTimes.size() - 1; ++i) {
        LinearLocation startLocation = endLocation;
        endLocation = locationIt.next();
        // it does not matter at all if this is accurate so long as it is consistent
        for (int j = last; j < startLocation.getSegmentIndex(); ++j) {
            Coordinate from = shape.getCoordinateN(j);
            Coordinate to = shape.getCoordinateN(j + 1);
            double xd = from.x - to.x;
            double yd = from.y - to.y;
            distanceSoFar += FastMath.sqrt(xd * xd + yd * yd);
        }
        last = startLocation.getSegmentIndex();
        double startIndex = distanceSoFar + startLocation.getSegmentFraction() * startLocation.getSegmentLength(shape);
        // advance distanceSoFar up to start of segment containing endLocation
        for (int j = last; j < endLocation.getSegmentIndex(); ++j) {
            Coordinate from = shape.getCoordinateN(j);
            Coordinate to = shape.getCoordinateN(j + 1);
            double xd = from.x - to.x;
            double yd = from.y - to.y;
            distanceSoFar += FastMath.sqrt(xd * xd + yd * yd);
        }
        last = startLocation.getSegmentIndex();
        double endIndex = distanceSoFar + endLocation.getSegmentFraction() * endLocation.getSegmentLength(shape);
        ShapeSegmentKey key = new ShapeSegmentKey(shapeId, startIndex, endIndex);
        LineString geometry = _geometriesByShapeSegmentKey.get(key);
        if (geometry == null) {
            LocationIndexedLine locationIndexed = new LocationIndexedLine(shape);
            geometry = (LineString) locationIndexed.extractLine(startLocation, endLocation);
            // Pack the resulting line string
            CoordinateSequence sequence = new PackedCoordinateSequence.Double(geometry.getCoordinates(), 2);
            geometry = _geometryFactory.createLineString(sequence);
        }
        geoms[i] = geometry;
    }
    return geoms;
}
Also used : CoordinateSequence(com.vividsolutions.jts.geom.CoordinateSequence) PackedCoordinateSequence(org.opentripplanner.common.geometry.PackedCoordinateSequence) AgencyAndId(org.onebusaway.gtfs.model.AgencyAndId) TransitStop(org.opentripplanner.routing.vertextype.TransitStop) Stop(org.onebusaway.gtfs.model.Stop) TransitStationStop(org.opentripplanner.routing.vertextype.TransitStationStop) LinearLocation(com.vividsolutions.jts.linearref.LinearLocation) TIntArrayList(gnu.trove.list.array.TIntArrayList) ArrayList(java.util.ArrayList) BogusShapeGeometryCaught(org.opentripplanner.graph_builder.annotation.BogusShapeGeometryCaught) TIntArrayList(gnu.trove.list.array.TIntArrayList) TIntList(gnu.trove.list.TIntList) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) StopTime(org.onebusaway.gtfs.model.StopTime) LocationIndexedLine(com.vividsolutions.jts.linearref.LocationIndexedLine) ShapePoint(org.onebusaway.gtfs.model.ShapePoint) LineString(com.vividsolutions.jts.geom.LineString) Coordinate(com.vividsolutions.jts.geom.Coordinate)

Aggregations

LinearLocation (com.vividsolutions.jts.linearref.LinearLocation)7 LocationIndexedLine (com.vividsolutions.jts.linearref.LocationIndexedLine)7 Coordinate (com.vividsolutions.jts.geom.Coordinate)4 LineString (com.vividsolutions.jts.geom.LineString)3 Geometry (com.vividsolutions.jts.geom.Geometry)2 ArrayList (java.util.ArrayList)2 P2 (org.opentripplanner.common.model.P2)2 Edge (org.opentripplanner.routing.graph.Edge)2 CoordinateSequence (com.vividsolutions.jts.geom.CoordinateSequence)1 Envelope (com.vividsolutions.jts.geom.Envelope)1 AssertionFailedException (com.vividsolutions.jts.util.AssertionFailedException)1 TIntList (gnu.trove.list.TIntList)1 TIntArrayList (gnu.trove.list.array.TIntArrayList)1 HashSet (java.util.HashSet)1 LinkedList (java.util.LinkedList)1 List (java.util.List)1 AgencyAndId (org.onebusaway.gtfs.model.AgencyAndId)1 ShapePoint (org.onebusaway.gtfs.model.ShapePoint)1 Stop (org.onebusaway.gtfs.model.Stop)1 StopTime (org.onebusaway.gtfs.model.StopTime)1