0

I have a point (defined by latitude and longitude) and a line (defined by latitude start+longitude start and latitude end+longitude end).
The line is always in the direction start => end, but of course the line can be in any angle of the compass. I only know, my "path" is from "start" to "end" and my position (point) can be on the path, on the left side of the path or on the right side of the path.

And now my problem is to know on which side of the path is the point. And I really don't have any idea how to get this information.

I already searched for this algoritm and I found many answer, all based on the formula:

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

for example in this post: How to tell whether a point is to the right or left side of a line
Unfortunately, if does not work as expected if I use latitude and longitude (I always get the point is "left" of the path) and I really don't understand where is the error...

My code is:

  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;
  }

Can someone explain me where is my error?

Thank you very very much and happy easter! Luca

  • Is _left_ or _right_ really applicable here? On the globe, the direction between two objects can be defined as "to the east" or "to the west". – Nowhere Man Apr 03 '21 at 19:04
  • Of course, it is applicable! I have a path and I'm a (moving) point. I can turn left or right to "reach" the path, not important if I'll move south, nord, east or west. If I'm on a airplane and want to reach my route I can turn left or right. I hope I could explain what I mean... – Luca Bertoncello Apr 03 '21 at 19:12

3 Answers3

1

Find the angle from the start point to the end point. Then find the angle to your point. If the angle is smaller than the angle to the end point, the point is to the left of the line. If it is larger, the point is to the right of the line.

I hope this helps you solve your problem. Happy Easter too.

P.S.: Calculate angle between two Latitude/Longitude points

1

The equation you provided works fine in a two dimensional plane, but not with geographical coordinates.

You can convert latitude and longitude to a two dimensional point using several approaches.

The de facto standard for Web mapping applications is the Web Mercator projection.

You can find a very handy implementation of the algorithm here in both Java and C++, developed by the University of Eindhoven.

The algorithm is implemented in the WebMercator class. Please, consider this implementation adapted for your needs:

/**
 * 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
    );
  }

}

Both GeoPoint and Point are simple JavaBean classes:

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;
  }
}

The idea is to convert first your geographical coordinates to its representation in a 2D plain using the provided code, and then use your algorithm. Please, consider the following test case:

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 you run the program, it will give you the desired output:

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.
jccampanero
  • 50,989
  • 3
  • 20
  • 49
  • Unfortunately it does **not** work... I transformed all coordinates with your class and applyed my algorithm, but the answer is always "right" (solution of the equation is always **negative**). Other idea? It **must** be an error somewhere in my code. I cannot believe, the whole world use a wrong algorithm... – Luca Bertoncello Apr 04 '21 at 06:28
  • I @LucaBertoncello. I am sorry to hear that it does not work. Please, can you further explain how are you testing it? I updated my answer with a new version of the `WebMercator` class, and with a simple test case. Please, can you test it? – jccampanero Apr 04 '21 at 10:33
0

OK, people!
Definitively I need a long vacation...

I found the problem and the problem was not the formula nor the conversion (apropos: in osmdroid I have an integrated class to convert the coordinates in screen points, so I don't need external class to convert the coordinates with mercator or other methods).

The problem was, that I got the wrong points of my path (all shifted one position above), so this is not a surprise that the calculation returned "wrong" data...

Shame on myself...

Happy easter monday and thank you very very much for your help!
Luca

  • That is great Luca. The important thing is that you found the reason of your problem. If this is the correct answer, please, mark it as such. If some other answers were helpful, consider upvote them. Happy Easter to you as well mate. – jccampanero Apr 04 '21 at 19:30