21

I'm working on a "compass" for a mobile-device. I have the following points:

point 1 (current location): Latitude = 47.2246, Longitude = 8.8257
point 2 (target  location): Latitude = 50.9246, Longitude = 10.2257

Also I have the following information (from my android-phone):

The compass-direction in degree, which bears to the north. 
For example, when I direct my phone to north, I get 0°

How can I create a "compass-like" arrow which shows me the direction to the point?

Is there a mathematic-problem for this?

EDIT: Okay I found a solution, it looks like this:

/**
 * Params: lat1, long1 => Latitude and Longitude of current point
 *         lat2, long2 => Latitude and Longitude of target  point
 *         
 *         headX       => x-Value of built-in phone-compass
 * 
 * Returns the degree of a direction from current point to target point
 *
 */
function getDegrees(lat1, long1, lat2, long2, headX) {
    
    var dLat = toRad(lat2-lat1);
    var dLon = toRad(lon2-lon1);

    lat1 = toRad(lat1);
    lat2 = toRad(lat2);

    var y = Math.sin(dLon) * Math.cos(lat2);
    var x = Math.cos(lat1)*Math.sin(lat2) -
            Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
    var brng = toDeg(Math.atan2(y, x));

    // fix negative degrees
    if(brng<0) {
        brng=360-Math.abs(brng);
    }

    return brng - headX;
}
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
eav
  • 2,123
  • 7
  • 25
  • 34
  • 1
    Do not post the solution about which even you are not clear. – Tofeeq Ahmad Sep 14 '12 at 15:49
  • @Sameer what's your problem with the solution? If nobodoy else posts one, I can be the one. It's only for reference, if somebody else needs it. Getting a down-vote for that "reason". Can't understand. – eav Sep 17 '12 at 07:24
  • @eav What does the toRad function you created look like? – gohnjanotis Jun 15 '14 at 00:55
  • @gohnjanotis: Not that much magic behind -> http://stackoverflow.com/questions/135909/what-is-the-method-for-converting-radians-to-degrees – eav Jun 16 '14 at 07:45
  • @eav If you want to post a solution to your own question, post it as an answer, not as an edit to the question. Answering your own question is perfectly normal and you can mark it as accepted to bring attention to it as the solution you went with the same as any other answer (it just won't earn any points from that). It's just much clearer to other users that come looking for solutions to similar problems if you do it this way. – Vala Jul 17 '14 at 20:40
  • 1
    The function declaration has long1 and long2 but when used in the function they are lon1 and lon2. – John Phillips Dec 05 '15 at 12:57
  • long2 !== lon2. – digout Feb 24 '18 at 12:03

4 Answers4

19

O forgot to say I found the answer eventually. The application is to determine compass direction of a transit vehicle and its destination. Essentially, fancy math for acquiring curvature of Earth, finding an angle/compass reading, and then matching that angle with a generic compass value. You could of course just keep the compassReading and apply that as an amount of rotation for your image. Please note this is an averaged determination of the vehicle direction to the end point (bus station) meaning it can't know what the road is doing (so this probably best applies to airplanes or roller derby).

//example obj data containing lat and lng points
//stop location - the radii end point
endpoint.lat = 44.9631;
endpoint.lng = -93.2492;

//bus location from the southeast - the circle center
startpoint.lat = 44.95517;
startpoint.lng = -93.2427;

function vehicleBearing(endpoint, startpoint) {
    endpoint.lat = x1;
    endpoint.lng = y1;
    startpoint.lat = x2;
    startpoint.lng = y2;

    var radians = getAtan2((y1 - y2), (x1 - x2));

    function getAtan2(y, x) {
        return Math.atan2(y, x);
    };

    var compassReading = radians * (180 / Math.PI);

    var coordNames = ["N", "NE", "E", "SE", "S", "SW", "W", "NW", "N"];
    var coordIndex = Math.round(compassReading / 45);
    if (coordIndex < 0) {
        coordIndex = coordIndex + 8
    };

    return coordNames[coordIndex]; // returns the coordinate value
}

ie: vehicleBearing(mybus, busstation) might return "NW" means its travelling northwesterly

Md Mohsin
  • 1,568
  • 1
  • 18
  • 28
ericjam
  • 1,501
  • 2
  • 20
  • 30
  • @liquified, sorry am unable to understand why (compassReading / 45) is being done. Could you please let me know. Thanks. – user2071152 Sep 24 '15 at 22:50
  • 45 is 45 degree intervals of the compass, correlating to the 9 coordNames (human names) of the compass array (NSEW,etc). Dividing your compassReading by 45 gives you, in the range of 360 degrees, how close your reading is to one of those intervals (Math.round). 360 / 45 for example gives you 8 which is the 8th index in the array or "N". You could modify this if you wanted only NSEW by dividing by 90 instead – ericjam Sep 25 '15 at 14:47
  • @efwjames " correlating to the 9 coordNames " - 45*8=360. You have repeated "N" twice in your coordNames array. Please remove that. – Mandeep Janjua Jul 12 '19 at 18:54
  • I vote for this answer, and give you my slightly shortened version (I like arrays, and more winds): const cardinals = ["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW","N"]; const carDirect = (x0,y0, x1,y1) => Math.round( Math.atan2((x1-x0),(y1-y0)) * (8 / Math.PI) ); const cardIndex = (dir) => dir<0 ? dir+16 : dir; console.log(cardinals[cardIndex(carDirect( start_lng,start_lat, end_lng,end_lat))]); Thank you. – allez l'OM Nov 06 '21 at 16:01
2

I found some useful gps coordinates formula in math here. For this case, here my solution

 private double getDirection(double lat1, double lng1, double lat2, double lng2) {

    double PI = Math.PI;
    double dTeta = Math.log(Math.tan((lat2/2)+(PI/4))/Math.tan((lat1/2)+(PI/4)));
    double dLon = Math.abs(lng1-lng2);
    double teta = Math.atan2(dLon,dTeta);
    double direction = Math.round(Math.toDegrees(teta));
    return direction; //direction in degree

}
deya tri
  • 43
  • 1
  • 10
0

I couldn't understand your solution well, calculating the slope worked for me. To modify on efwjames's and your answer. This should do -

import math
def getDegrees(lat1, lon1, lat2, lon2,head):
    dLat = math.radians(lat2-lat1)
    dLon = math.radians(lon2-lon1)
    bearing = math.degrees(math.atan2(dLon, dLat))
    return head-bearing
Vivek
  • 322
  • 1
  • 13
-3

You'd need to calculate an Euclidean vector between your start point and end point, then calculate its angle (let's say relative to positive X) which would be the angle you want to rotate your arrow by.

StaWho
  • 2,488
  • 17
  • 24
  • 1
    or you can go all fancy and account for the curvature of the Earth – StaWho Dec 14 '11 at 10:30
  • 3
    That's a great description of an answer, try maybe providing a real example. – ericjam Jan 03 '13 at 20:41
  • @liquified - if you look the post, even before edit, it is not tagged with any language and the question asked is: `Is there a mathematic-problem for this?`. There is also no code supplied and apart from `math` tag there's also no other indication if OP would like to resolve it (amongst many) via, let's say real numbers trigonometry or complex numbers trigonometry. Given the ambiguous parameters of the question I think the answer is sufficient. – StaWho Jan 04 '13 at 07:14