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.