【问题标题】:Finding if point is left or right to a line (using coordinates!!)查找点是在一条线的左边还是右边(使用坐标!!)
【发布时间】:2021-04-03 18:49:32
【问题描述】:

我有一个点(由纬度和经度定义)和一条线(由纬度起点+经度起点和纬度终点+经度终点定义)。
这条线总是在 start => end 的方向上,但当然这条线可以在罗盘的任何角度。我只知道,我的“路径”是从“开始”到“结束”,我的位置(点)可以在路径上,路径的左侧或路径的右侧。

现在我的问题是知道路径的哪一边是重点。而且我真的不知道如何获取这些信息。

我已经搜索过这个算法,我找到了很多答案,都是基于公式:

((b.X - a.X)*(c.Y - a.Y) - (b.Y - a.Y)*(c.X - a.X)) > 0

例如在这篇文章中:How to tell whether a point is to the right or left side of a line
不幸的是,如果我使用纬度和经度 not 是否按预期工作(我总是得到点是路径的“左侧”)并且我真的不明白错误在哪里......

我的代码是:

  private int isPointLeftOrRight(GeoPoint point, GeoPoint lineStart, GeoPoint lineEnd)
  {
    double ret = (lineEnd.getLongitude() - lineStart.getLongitude()) * (point.getLatitude() - lineStart.getLatitude()) -
                 (lineEnd.getLatitude() - lineStart.getLatitude()) * (point.getLongitude() - lineStart.getLongitude());
    if(ret == 0)
      return 0;
    return (ret > 0) ? 1 : -1;
  }

谁能解释我的错误在哪里?

非常感谢你,复活节快乐! 卢卡

【问题讨论】:

  • leftright 真的适用于此吗?在地球仪上,两个物体之间的方向可以定义为“向东”或“向西”。
  • 当然适用!我有一条路,我是一个(移动的)点。我可以左转或右转以“到达”路径,如果我向南、向北、向东或向西移动并不重要。如果我在飞机上并且想到达我的路线,我可以左转或右转。我希望我能解释一下我的意思...

标签: java line point


【解决方案1】:

您提供的方程式在二维平面上可以正常工作,但在地理坐标上却不行。

您可以使用多种方法将纬度和经度转换为二维点。

Web 地图应用程序的实际标准是Web Mercator projection

您可以在 Java 和 C++ 中找到算法 here 的一个非常方便的实现,由 University of Eindhoven 开发。

算法在WebMercator 类中实现。请考虑适合您的需求的实现:

/**
 * Functions for projecting and unprojecting coordinates and points using the
 * Web Mercator projection. The code in this class is based directly off of the
 * code in the Leaflet library (@url http://leafletjs.com/).
 *
 * @url https://github.com/Leaflet/Leaflet/blob/master/src/geo/projection/Projection.SphericalMercator.js
 *
 * This class provides a spherical mercator projection, which is used by the
 * `EPSG:3857` CRS and most online maps.
 *
 * Points are in a space where the origin is in the upper left corner. Thus, the
 * tile on the lowest zoom level (z = 0) looks as follows.
 *
 *
 *                               longitude > >
 *
 *   (0, 0)                (128, 0)               (256, 0)
 *          +-----------------+-----------------+
 *          |                 |                 |
 *          |                 |                 |
 *          |                 |                 |
 *          |                 |                 |     ^
 *          |                 |                 |     ^
 *          |                 |                 |  latitude
 *          |                 |                 |
 *          |                 | (128, 128)      |
 * (0, 128) +-----------------+-----------------+ (256, 128)
 *          |                 | (0LNG, 0LAT)    |
 *          |                 |                 |
 *          |                 |                 |
 *          |                 |                 |
 *          |                 |                 |
 *          |                 |                 |
 *          |                 |                 |
 *          |                 |                 |
 *          +-----------------+-----------------+
 * (0, 256)               (128, 256)              (256, 256)
 */
public class WebMercator {

  private static final double R = 6378137.0;
  private static final double TRANSFORM_SCALE = (0.5 / (Math.PI * R));

  /**
   * Projects coordinates to a 2D point using spherical Mercator projection.
   */
  public static Point geoPointToPoint(GeoPoint ll, int zoom) {
    Point projectedPoint = project(ll.getLatitude(), ll.getLongitude());
    return transform(projectedPoint, scale(zoom));
  }

  /**
   * Unprojects a 2D point to coordinates using spherical Mercator projection.
   */
  public static GeoPoint pointToGeoPoint(Point p, int zoom) {
    Point untransformedPoint = untransform(p, scale(zoom));
    return unproject(untransformedPoint);
  }


  private static int scale(int zoom) {
    // 256 * 2^zoom = 2^8 * 2^zoom = 2^(8 + zoom)
    return 1 << (8 + zoom);
  }

  private static Point project(double lat, double lng) {
    double d = Math.PI / 180.0;
    double max = 1 - 1E-15;
    double sin = Math.max(Math.min(Math.sin(lat * d), max), -max);
    return new Point(
        WebMercator.R * lng * d,
        WebMercator.R * Math.log((1 + sin) / (1 - sin)) / 2
    );
  }

  private static GeoPoint unproject(Point p) {
    double d = 180.0 / Math.PI;
    return new GeoPoint(
        (2 * Math.atan(Math.exp(p.getY() / WebMercator.R)) - (Math.PI / 2)) * d,
        p.getX() * d / WebMercator.R
    );
  }

  /*
   * The below transformation functions are based off of the Leaflet
   * transformation with parameters [ WebMercator.TRANSFORM_SCALE, 0.5,
   *                                 -WebMercator.TRANSFORM_SCALE, 0.5].
   */

  private static Point transform(Point p, int scale) {
    return new Point(
        scale * (WebMercator.TRANSFORM_SCALE * p.getX() + 0.5),
        scale * (-WebMercator.TRANSFORM_SCALE * p.getY() + 0.5)
    );
  }

  private static Point untransform(Point p, int scale) {
    double dScale = (double) scale;
    return new Point(
        (p.getX() / dScale - 0.5) / WebMercator.TRANSFORM_SCALE,
        (p.getY() / dScale - 0.5) / -WebMercator.TRANSFORM_SCALE
    );
  }

}

GeoPointPoint 都是简单的 JavaBean 类:

public class GeoPoint {
  private double latitude;
  private double longitude;

  public GeoPoint(double latitude, double longitude) {
    this.latitude = latitude;
    this.longitude = longitude;
  }

  public double getLatitude() {
    return latitude;
  }

  public void setLatitude(double latitude) {
    this.latitude = latitude;
  }

  public double getLongitude() {
    return longitude;
  }

  public void setLongitude(double longitude) {
    this.longitude = longitude;
  }
}
public class Point {
  private double x;
  private double y;

  public Point(double x, double y) {
    this.x = x;
    this.y = y;
  }

  public double getX() {
    return x;
  }

  public void setX(double x) {
    this.x = x;
  }

  public double getY() {
    return y;
  }

  public void setY(double y) {
    this.y = y;
  }
}

我们的想法是首先使用提供的代码将您的地理坐标转换为其在 2D 平面中的表示,然后使用您的算法。请考虑以下测试用例:

public class GeoPointMain {

  public static void main(String... args) {
    GeoPointMain main = new GeoPointMain();
    GeoPoint newYork = new GeoPoint(40.730610, -73.935242);
    GeoPoint madrid = new GeoPoint(40.416729, -3.703339);
    GeoPoint canaryIslands = new GeoPoint(28.291565, -16.629129);
    GeoPoint iceland = new GeoPoint(64.9313, -19.0212);

    System.out.printf("If I am traveling from New York to Madrid and my plane is over the Canary Islands it is on the %s.\n",
        ( main.isPointLeftOrRight(
          WebMercator.geoPointToPoint(canaryIslands, 0),
          WebMercator.geoPointToPoint(newYork,0),
          WebMercator.geoPointToPoint(madrid,0)
        ) == 1 ? "right" : "left" )
    );

    System.out.printf("If I am traveling from New York to Madrid and my plane is over Iceland it is on the %s.\n",
        ( main.isPointLeftOrRight(
            WebMercator.geoPointToPoint(iceland, 0),
            WebMercator.geoPointToPoint(newYork,0),
            WebMercator.geoPointToPoint(madrid,0)
        ) == 1 ? "right" : "left" )
    );
  }

  private int isPointLeftOrRight(Point point, Point lineStart, Point lineEnd) {
    double ret = (lineEnd.getX() - lineStart.getX()) * (point.getY() - lineStart.getY()) -
        (lineEnd.getY() - lineStart.getY()) * (point.getX() - lineStart.getX());
    if(ret == 0)
      return 0;
    return (ret > 0) ? 1 : -1;
  }
}

如果你运行程序,它会给你想要的输出:

If I am traveling from New York to Madrid and my plane is over the Canary Islands it is on the right.
If I am traveling from New York to Madrid and my plane is over Iceland it is on the left.

【讨论】:

  • 不幸的是它不起作用...我用你的班级转换了所有坐标并应用了我的算法,但答案总是“正确”(方程的解总是否定)。其他想法?它必须是我代码中某处的错误。我不敢相信,全世界都在使用错误的算法......
  • 我@LucaBertoncello。我很遗憾听到它不起作用。拜托,你能进一步解释一下你是如何测试它的吗?我用WebMercator 类的新版本和一个简单的测试用例更新了我的答案。请问,你能测试一下吗?
【解决方案2】:

求起点到终点的角度。然后找到你点的角度。如果角度小于到端点的角度,则该点位于线的左侧。如果较大,则该点位于该线的右侧。

我希望这可以帮助您解决问题。复活节快乐。

附注:Calculate angle between two Latitude/Longitude points

【讨论】:

    【解决方案3】:

    好的,伙计们!
    毫无疑问,我需要一个长假...

    我发现了问题,问题是不是公式也不是转换(适当:在osmdroid中我有一个集成类来转换屏幕点中的坐标,所以我不需要外部类用墨卡托或其他方法转换坐标)。

    问题是,我得到了路径的错误点(全部在上方移动了一个位置),因此计算返回“错误”数据也就不足为奇了... p>

    为自己感到羞耻...

    复活节星期一快乐,非常感谢您的帮助!
    卢卡

    【讨论】:

    • 卢卡太好了。重要的是你找到了问题的原因。如果这是正确答案,请标记为正确答案。如果其他一些答案有帮助,请考虑支持它们。祝你复活节快乐。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多