All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.graph_builder.module.geometry.IndexedLineSegment Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.graph_builder.module.geometry;

import org.locationtech.jts.geom.Coordinate;
import org.opentripplanner.framework.geometry.SphericalDistanceLibrary;

/** TODO Move this stuff into the geometry library */
class IndexedLineSegment {

  private static final double RADIUS = SphericalDistanceLibrary.RADIUS_OF_EARTH_IN_M;
  private final double lineLength;
  int index;
  Coordinate start;
  Coordinate end;

  public IndexedLineSegment(int index, Coordinate start, Coordinate end) {
    this.index = index;
    this.start = start;
    this.end = end;
    this.lineLength = SphericalDistanceLibrary.fastDistance(start, end);
  }

  public double fraction(Coordinate coord) {
    double cte = crossTrackError(coord);
    double distanceToStart = SphericalDistanceLibrary.fastDistance(coord, start);
    double distanceToEnd = SphericalDistanceLibrary.fastDistance(coord, end);

    if (cte < distanceToStart && cte < distanceToEnd) {
      double atd = alongTrackDistance(coord, cte);
      return atd / lineLength;
    } else {
      if (distanceToStart < distanceToEnd) {
        return 0;
      } else {
        return 1;
      }
    }
  }

  // in radians
  static double bearing(Coordinate c1, Coordinate c2) {
    double deltaLon = ((c2.x - c1.x) * Math.PI) / 180;
    double lat1Radians = (c1.y * Math.PI) / 180;
    double lat2Radians = (c2.y * Math.PI) / 180;
    double y = Math.sin(deltaLon) * Math.cos(lat2Radians);
    double x =
      Math.cos(lat1Radians) * Math.sin(lat2Radians) -
      Math.sin(lat1Radians) * Math.cos(lat2Radians) * Math.cos(deltaLon);
    return Math.atan2(y, x);
  }

  double crossTrackError(Coordinate coord) {
    double distanceFromStart = SphericalDistanceLibrary.fastDistance(start, coord);
    double bearingToCoord = bearing(start, coord);
    double bearingToEnd = bearing(start, end);
    return (
      Math.asin(Math.sin(distanceFromStart / RADIUS) * Math.sin(bearingToCoord - bearingToEnd)) *
      RADIUS
    );
  }

  double distance(Coordinate coord) {
    double cte = crossTrackError(coord);
    double atd = alongTrackDistance(coord, cte);
    double inverseAtd = inverseAlongTrackDistance(coord, -cte);
    double distanceToStart = SphericalDistanceLibrary.fastDistance(coord, start);
    double distanceToEnd = SphericalDistanceLibrary.fastDistance(coord, end);

    if (distanceToStart < distanceToEnd) {
      //we might be behind the line start
      if (inverseAtd > lineLength) {
        //we are behind line start
        return distanceToStart;
      } else {
        //we are within line
        return Math.abs(cte);
      }
    } else {
      //we might be after line end
      if (atd > lineLength) {
        //we are behind line end, so we that's the nearest point
        return distanceToEnd;
      } else {
        //we are within line
        return Math.abs(cte);
      }
    }
  }

  private double inverseAlongTrackDistance(Coordinate coord, double inverseCrossTrackError) {
    double distanceFromEnd = SphericalDistanceLibrary.fastDistance(end, coord);
    return (
      Math.acos(Math.cos(distanceFromEnd / RADIUS) / Math.cos(inverseCrossTrackError / RADIUS)) *
      RADIUS
    );
  }

  private double alongTrackDistance(Coordinate coord, double crossTrackError) {
    double distanceFromStart = SphericalDistanceLibrary.fastDistance(start, coord);
    return (
      Math.acos(Math.cos(distanceFromStart / RADIUS) / Math.cos(crossTrackError / RADIUS)) * RADIUS
    );
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy