Beispiel #1
0
  /**
   * Find a location on an existing street near the given point, without actually creating any
   * vertices or edges.
   *
   * @return a new Split object, or null if no edge was found in range.
   */
  public static Split find(
      double lat,
      double lon,
      double searchRadiusMeters,
      StreetLayer streetLayer,
      StreetMode streetMode) {

    // After this conversion, the entire geometric calculation is happening in fixed precision int
    // degrees.
    int fixedLat = VertexStore.floatingDegreesToFixed(lat);
    int fixedLon = VertexStore.floatingDegreesToFixed(lon);

    // We won't worry about the perpendicular walks yet.
    // Just insert or find a vertex on the nearest road and return that vertex.

    final double metersPerDegreeLat = 111111.111;
    double cosLat =
        FastMath.cos(FastMath.toRadians(lat)); // The projection factor, Earth is a "sphere"
    // Use longs for radii and their square because squaring the fixed-point radius _will_ overflow
    // a signed int32.
    long radiusFixedLat =
        VertexStore.floatingDegreesToFixed(searchRadiusMeters / metersPerDegreeLat);
    long radiusFixedLon =
        (int) (radiusFixedLat / cosLat); // Expand the X search space, don't shrink it.
    Envelope envelope = new Envelope(fixedLon, fixedLon, fixedLat, fixedLat);
    envelope.expandBy(radiusFixedLon, radiusFixedLat);
    long squaredRadiusFixedLat = radiusFixedLat * radiusFixedLat;
    EdgeStore.Edge edge = streetLayer.edgeStore.getCursor();
    // Iterate over the set of forward (even) edges that may be near the given coordinate.
    TIntCollection candidateEdges = streetLayer.findEdgesInEnvelope(envelope);
    // The split location currently being examined and the best one seen so far.
    Split curr = new Split();
    Split best = new Split();
    candidateEdges.forEach(
        e -> {
          curr.edge = e;
          edge.seek(e);
          // Skip Link edges those are links between transit stops/P+R/Bike share vertices and graph
          // Without this origin or destination point can link to those edges because they have ALL
          // permissions
          // and route is never found since point is inaccessible because edges leading to it don't
          // have required permission
          if (edge.getFlag(EdgeStore.EdgeFlag.LINK)) return true;

          // If an edge does not allow traversal with the specified mode, skip over it.
          if (streetMode == StreetMode.WALK && !edge.getFlag(EdgeStore.EdgeFlag.ALLOWS_PEDESTRIAN))
            return true;
          if (streetMode == StreetMode.BICYCLE && !edge.getFlag(EdgeStore.EdgeFlag.ALLOWS_BIKE))
            return true;
          if (streetMode == StreetMode.CAR && !edge.getFlag(EdgeStore.EdgeFlag.ALLOWS_CAR))
            return true;

          // The distance to this edge is the distance to the closest segment of its geometry.
          edge.forEachSegment(
              (seg, fixedLat0, fixedLon0, fixedLat1, fixedLon1) -> {
                // Find the fraction along the current segment
                curr.seg = seg;
                curr.frac =
                    GeometryUtils.segmentFraction(
                        fixedLon0, fixedLat0, fixedLon1, fixedLat1, fixedLon, fixedLat, cosLat);
                // Project to get the closest point on the segment.
                // Note: the fraction is scaleless, xScale is accounted for in the segmentFraction
                // function.
                curr.fixedLon = (int) (fixedLon0 + curr.frac * (fixedLon1 - fixedLon0));
                curr.fixedLat = (int) (fixedLat0 + curr.frac * (fixedLat1 - fixedLat0));
                // Find squared distance to edge (avoid taking root)
                long dx = (long) ((curr.fixedLon - fixedLon) * cosLat);
                long dy = (long) (curr.fixedLat - fixedLat);
                curr.distSquared = dx * dx + dy * dy;
                // Ignore segments that are too far away (filter false positives).
                if (curr.distSquared < squaredRadiusFixedLat) {
                  if (curr.distSquared < best.distSquared) {
                    // Update the best segment if we've found something closer.
                    best.setFrom(curr);
                  } else if (curr.distSquared == best.distSquared && curr.edge < best.edge) {
                    // Break distance ties by favoring lower edge IDs. This makes destination
                    // linking
                    // deterministic where centroids are equidistant to edges (see issue #159).
                    best.setFrom(curr);
                  }
                }
              });
          // The loop over the edges should continue.
          return true;
        });

    if (best.edge < 0) {
      // No edge found nearby.
      return null;
    }

    // We found an edge. Iterate over its segments again, accumulating distances along its geometry.
    // The distance calculations involve square roots so are deferred to happen here, only on the
    // selected edge.
    // TODO accumulate before/after geoms. Split point can be passed over since it's not an
    // intermediate.
    // The length is are stored in one-element array to dodge Java's "effectively final" BS.
    edge.seek(best.edge);
    best.vertex0 = edge.getFromVertex();
    best.vertex1 = edge.getToVertex();
    double[] lengthBefore_fixedDeg = new double[1];
    edge.forEachSegment(
        (seg, fLat0, fLon0, fLat1, fLon1) -> {
          // Sum lengths only up to the split point.
          // lengthAfter should be total length minus lengthBefore, which ensures splits do not
          // change total lengths.
          if (seg <= best.seg) {
            double dx = (fLon1 - fLon0) * cosLat;
            double dy = (fLat1 - fLat0);
            double length = FastMath.sqrt(dx * dx + dy * dy);
            if (seg == best.seg) {
              length *= best.frac;
            }
            lengthBefore_fixedDeg[0] += length;
          }
        });
    // Convert the fixed-precision degree measurements into (milli)meters
    double lengthBefore_floatDeg =
        VertexStore.fixedDegreesToFloating((int) lengthBefore_fixedDeg[0]);
    best.distance0_mm = (int) (lengthBefore_floatDeg * metersPerDegreeLat * 1000);
    // FIXME perhaps we should be using the sphericalDistanceLibrary here, or the other way around.
    // The initial edge lengths are set using that library on OSM node coordinates, and they are
    // slightly different.
    // We are using a single cosLat value at the linking point, instead of a different value at each
    // segment.
    if (best.distance0_mm < 0) {
      best.distance0_mm = 0;
      LOG.error("Length of first street segment was not positive.");
    }

    if (best.distance0_mm > edge.getLengthMm()) {
      // This mistake happens because the linear distance calculation we're using comes out longer
      // than the
      // spherical distance. The graph remains coherent because we force the two split edge lengths
      // to add up
      // to the original edge length.
      LOG.debug(
          "Length of first street segment was greater than the whole edge ({} > {}).",
          best.distance0_mm,
          edge.getLengthMm());
      best.distance0_mm = edge.getLengthMm();
    }
    best.distance1_mm = edge.getLengthMm() - best.distance0_mm;
    return best;
  }
Beispiel #2
0
  /**
   * Find a split on a particular edge. FIXME this appears to be copy-pasted from another method and
   * only used for park and rides. Can we reuse some code here?
   */
  public static Split findOnEdge(double lat, double lon, EdgeStore.Edge edge) {

    // After this conversion, the entire geometric calculation is happening in fixed precision int
    // degrees.
    int fixedLat = VertexStore.floatingDegreesToFixed(lat);
    int fixedLon = VertexStore.floatingDegreesToFixed(lon);

    // We won't worry about the perpendicular walks yet.
    // Just insert or find a vertex on the nearest road and return that vertex.

    final double metersPerDegreeLat = 111111.111;
    double cosLat =
        FastMath.cos(FastMath.toRadians(lat)); // The projection factor, Earth is a "sphere"

    // TODO copy paste code
    // The split location currently being examined and the best one seen so far.
    Split curr = new Split();
    Split best = new Split();
    curr.edge = edge.edgeIndex;

    best.vertex0 = edge.getFromVertex();
    best.vertex1 = edge.getToVertex();
    double[] lengthBefore_fixedDeg = new double[1];
    edge.forEachSegment(
        (seg, fixedLat0, fixedLon0, fixedLat1, fixedLon1) -> {
          // Find the fraction along the current segment
          curr.seg = seg;
          curr.frac =
              GeometryUtils.segmentFraction(
                  fixedLon0, fixedLat0, fixedLon1, fixedLat1, fixedLon, fixedLat, cosLat);
          // Project to get the closest point on the segment.
          // Note: the fraction is scaleless, xScale is accounted for in the segmentFraction
          // function.
          curr.fixedLon = (int) (fixedLon0 + curr.frac * (fixedLon1 - fixedLon0));
          curr.fixedLat = (int) (fixedLat0 + curr.frac * (fixedLat1 - fixedLat0));

          double dx = (fixedLon1 - fixedLon0) * cosLat;
          double dy = (fixedLat1 - fixedLat0);
          double length = FastMath.sqrt(dx * dx + dy * dy);

          curr.distance0_mm =
              (int) ((lengthBefore_fixedDeg[0] + length * curr.frac) * metersPerDegreeLat * 1000);

          lengthBefore_fixedDeg[0] += length;

          curr.distSquared = (long) (dx * dx + dy * dy);
          // Replace the best segment if we've found something closer.
          if (curr.distSquared < best.distSquared) {
            best.setFrom(curr);
          }
        }); // end loop over segments

    int edgeLengthMm = edge.getLengthMm();
    if (best.distance0_mm > edgeLengthMm) {
      // rounding errors
      best.distance0_mm = edgeLengthMm;
      best.distance1_mm = 0;
    } else {
      best.distance1_mm = edgeLengthMm - best.distance0_mm;
    }

    return best;
  }