How can I determine direction with GPS readings alone and display in 360 degrees?
Solution 1
The formula you need (for lon1
, lon2
longitudes, and lat1
, lat2
latitudes of the two points) is given in many places - for example, at http://www.movable-type.co.uk/scripts/latlong.html . The math goes like this (convert it to your favorite language...):
dLon = lon2 - lon1;
y = sin(dLon) * cos(lat2);
x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) *cos(dLon);
bearing = atan2(y, x) * 180 / Pi;
Note: atan2
reduces the result to a value between [-pi, pi]. After multiplying by 180/pi
the number will be between [-180,180]. In order to get a value between 0 and 360 you could do, for example:
if (bearing < 0) bearing += 360;
I hope you can fill in any other details you need to make this work for you.
EDIT I have used the numbers your gave to write a little bit of code: this is Matlab, but it should be pretty readable:
% bearings
lat1 = 42.1234584;
lat2 = 42.1234583;
lon1 = -83.1234577;
lon2 = -83.1234510;
% convert to radians:
g2r = pi/180;
lat1r = lat1 * g2r;
lat2r = lat2 * g2r;
lon1r = lon1 * g2r;
lon2r = lon2 * g2r;
dlonr = lon2r - lon1r;
y = sin(dlonr) * cos(lat2r);
x = cos(lat1r)*sin(lat2r) - sin(lat1r) * cos(lat2r)*cos(dlonr);
% compute bearning and convert back to degrees:
bearing = atan2(y, x) / g2r;
fprintf(1,'x: %+.3e\ny: %+.3e\nbearing: %.3f\n', x, y, bearing);
This results in the output:
x: -1.745e-09
y: +8.673e-08
bearing: 91.153
As you can see, x
and y
are tiny - they represent the "fraction of the circumference of the earth" that you moved (about 3.5 meters due East, it would seem...). You should be able to debug your implementation with these numbers.
Notice by the way that GPS can have a poor "absolute" accuracy (in your case, uncertainty of > 100 m), yet still be good with "relative" accuracy (it measures the difference between two positions much better than 100 m).
Solution 2
if you are using the Location API then when your location changes you get a callback giving you a Location object which you can get the bearing
of the direction moved from 0-360 degrees
Related videos on Youtube
Catttdaddy
Updated on June 05, 2022Comments
-
Catttdaddy almost 2 years
I am working on an AS3 AIR for Android application. "my problem is this: given a GPS location, I want to retrieve the direction of movement (North, NW, West, SW, South, SE, East, NE)"
I would like the code for how to use 2 different GPS readings to deduce the direction of travel. I would then like to use that data for 360 degrees.
I think I understand how I can get general direction like North, South, East, and West. I would get 2 separate GPS readings and compare something like so...
See if X is greater in the 1st or 2nd GPS reading. See if Y is greater in the 1st or 2nd GPS reading. From this I should be able to see the general direction. Compare the differences of X1 and X2 with the difference of Y1 and Y2. then you can see what direction the user was headed in the most. Then display the direction.
Im not sure on how to take the data and use it for a 360 interpretation like a compass... can anyone help?
WORKING CODE-------------------------------------------------------------------------------
package { import flash.display.Sprite; // Geolocation sensor stuff import flash.sensors.Geolocation; import flash.events.GeolocationEvent; //Timer setup stuff for setting frequesncy of GPS Update import flash.utils.Timer; import flash.events.TimerEvent; // Sprite and Textfield display import flash.display.Sprite; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; public class FlashTest extends Sprite { //Variable to check if current or prior Geolocation Event fired private var gpsCheck:int = 1; public var dLon:Number //latitude and longitude in degrees (RAW info from Geolocation) public var gpsLat1:Number public var gpsLon1:Number private var gpsLat2:Number private var gpsLon2:Number public var bearing:Number // Latitude and longitude in radians converted from Degrees public var gpsLat1R:Number public var gpsLon1R:Number private var gpsLat2R:Number private var gpsLon2R:Number private var yy:Number private var xx:Number public function FlashTest() { // Text box for displaying results var my_txt:TextField = new TextField(); my_txt.wordWrap=true; my_txt.width = 300; my_txt.height = 300; addChild(my_txt); /* //If GPS is on device create Geolocation object named "my_geo" //Request updates from my_geo every 2000 milliseconds. Run onGeoUpdate function //when Event is triggered. After that create the text box for displaying data. if (Geolocation.isSupported) { var my_geo:Geolocation = new Geolocation(); my_geo.setRequestedUpdateInterval(2000); my_geo.addEventListener(GeolocationEvent.UPDATE, onGeoUpdate); var my_txt:TextField = new TextField(); my_txt.wordWrap=true; my_txt.width = 300; my_txt.height = 300; addChild(my_txt); } // If GPS is not supported on device display "No GPS Signal" else { addChild(my_txt); my_txt.wordWrap=true; my_txt.width = 300; my_txt.height = 300; addChild(my_txt); my_txt.text = "No GPS Signal "; } */ // False GPS reading being passed for testing //COMMENT THESE LINES OUT STARTING HERE--- gpsLat1 = 42.1234584; gpsLon1 = -83.1234577; gpsLat2 = 42.1234583; gpsLon2 = -83.1234577; // END OF WHAT SHOULD BE COMMENTED OUT--- // Equations to convert all RAW Geolocation information over to radians gpsLat1R = gpsLat1 * Math.PI / 180; gpsLon1R = gpsLon1 * Math.PI / 180; gpsLat2R = gpsLat2 * Math.PI / 180; gpsLon2R = gpsLon2 * Math.PI / 180; // The rest of the math dLon = gpsLon1 - gpsLon2; yy = Math.sin(dLon) * Math.cos(gpsLat2R); xx = Math.cos(gpsLat1R) * Math.sin(gpsLat2R) - Math.sin(gpsLat1R) * Math.cos(gpsLat2R) * Math.cos(dLon); bearing = Math.atan2(yy, xx) * 180 / Math.PI; // Run The Geoupdate function onGeoUpdate(); // onGeoUpdate basically displays the information that was collected and converted. // This is where you will put what you want the code to do with the results function onGeoUpdate():void { my_txt.text = "My Latitude is "+gpsLat1+ " and my Longitude is "+gpsLon1+ "My 2nd Latitude is "+gpsLat2+" and my 2nd Longitude is "+gpsLon2+ " Bearing is " +bearing; } } } }
-
Catttdaddy over 10 yearsWhen I use the Geolocation Object I am only getting the Lat/Lon with no decimal places. Its far to large to be able to get the bearing conveniently. When I look at the Geolocation in my map application I get at least 5 decimal places. Is it a property or something that will let me get most precise readings? I looked at horizontal and Vertical Accuracy but it doesnt match the informatino on other GPS apps. How can I get more precise GPS Info?
-
tyczj over 10 yearsthe only way you get a more precise info is by getting a better GPS lock. the better the lock the more decimal places that will get returned. To get the
Bearing
from the location object all you have to do islocation.getBearing()
in the callback when your location changes -
Catttdaddy over 10 yearsI dont see "location" in the Air API. I do have a Geolocation API. In the Geolocation API I have a "heading" property but it does not work for Android. Is the "location" API for Adobe AIR? where is it cause even a google search for "AIR API location" I only get "Geolocation"...
-
tyczj over 10 yearswhy do you need to use the AIR API for location just use the android API, it will probably solve most of your problems
-
Catttdaddy over 10 yearsBecause I am familiar with Actionscript 3 and not Java.
-
Catttdaddy over 10 yearsI think I got the code implemented. It all looks correct but all I am getting is a zero value.
-
Floris over 10 yearsCould you be using integer types anywhere? these are small numbers and small differences - you need a good floating point type (double) for the math to work... Can you show your code (maybe add to your question)? Are you sure there is a different in the longitude and latitudes? Can you print out the values of
x
andy
? -
Floris over 10 yearsIt all looks correct... except that the units of longitude and latitude probably don't "play nice" with your current use of the
sin
andcos
functions. The former are presumably in degrees, and the latter expect radians. You convert degrees to radians withrads=degs*Math.PI/180
; sometimes, a functionsind
andcosd
is built in which expects the argument in degrees. I would print outx
andy
to help in the debugging, for sure. -
Catttdaddy over 10 yearsIm getting LonR=-1.45xxxxxxxxxxxx79 and LatR=0.73xxxxxxxxxxx84
-
Catttdaddy over 10 yearsIm getting gpsLon2R=-1.45xxxxxxxxxxxx79 and gpsLat1R=0.73xxxxxxxxxxx84 I assume those are the radians. so my conversion from degrees to rad is working with gpsLat1R = gpsLat1 * Math.PI / 180; and gpsLon1R = gpsLon1 * Math.PI / 180; The bearing calculation must have a problem. My only difference from your code is where you have x = cos(lat1r)*sin(lat2r) - sin(lat1r) * cos(lat2r)*cos(dlon); I assumed "dlon" was a typo as you never mentioned "dlon" earlier in the code. you have only "dlonr".
-
Floris over 10 yearsSorry yes that was a typo. What do you get for the bearing when you fix the typo?
-
Catttdaddy over 10 yearsThe bearing is still "0"
-
Floris over 10 yearsHow strange. What do you get for
x
andy
? Does your platform even recognize theatan2
function? In some systems the parameters foratan2
are reversed - meaning that you needx,y
instead ofy,x
in order to get the right answer in those. You could also try usingatan2(y/x, 1)
(which works ifx != 0
) to see if rounding error in theatan2
function is the problem. What do you get foratan2(1,1)
andatan2(1e-10, 1e-14)
, for example? The answer should bepi/4
and approximatelypi/2
, respectively... -
Floris over 10 yearsFrom help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/… : "On Android devices, heading is not supported. The value of the heading property is always NaN (Not a Number)."
-
Floris over 10 yearsLooking at your code carefully, it seems like
gpsBearing()
is only called once - instead of being called after each update of the GPS coordinates. It needs to live inside youronGeoUpdate
function, doesn't it? -
Catttdaddy over 10 yearsX and Y are 0 when I trace them... I thought about bearings placement as well and it is not in the onGeoUpdate function on both sides of the if and then statement...
-
Floris over 10 yearsNot sure I understand your last sentence. "I thought about...". what did you conclude? You need to compute
bearing
each time the location updates. AddgpsHeading();
right beforegpsHeading = (bearing);
. And don't usegpsHeading = (e.heading);
in thegpsCheck == 2
case: it returns NaN on Android. -
Catttdaddy over 10 yearsSorry about the last comment. I was very tired. I meant to say I thought about it and changed the code. I just updated my code to the the current try... I moved gpsbearing(); inside of onGeoUpdate . I am still getting 0 for bearing. I am also getting zero for X and Y... Im lost as to where the problem is... I really appreciate your patience and help.
-
Floris over 10 yearsI'm stubborn, not touchy. Understand about comments made when tired. Can you create a standalone program that takes known numerical inputs (no geo events) and computes the bearing? Once you have that, litter your code with print statements to figure out where execution is not doing what you expect. I am not familiar with the IDE for Air, so I can't tell you how to step through your code, add breakpoints, etc - but that's what you will need to do once you have the standalone function working (something similar to my code snippet above). Can you confirm
gpsBearing()
is called? -
Catttdaddy over 10 yearsFrom what I can gather, it looks like I get everything until Math.cos and Math.sin are used. Im getting nothing for X,Y. Check it Here
-
Catttdaddy over 10 yearsIt looks like changing
dLon = gpsLon1R - gpsLon2R;
toodLon = gpsLon1 - gpsLon2;
makes it not produce zeroes. Im now getting a bearing of 65. Im not good enough with the math to say if it is right or not. I will have to wait to get on my comp to recompile and put it on my phone. Thanks for your help. -
Floris over 10 yearsIt was helpful that you created the
wonderfl
example. It turns out that variablesx
andy
had a built in (inherited) meaning (I was wondering how you got away with not declaring them). Please see my working example code at wonderfl.net/c/bFRa ... I took thegeoUpdate()
function out to inline everything (because I was confused about the scope ofx
andy
in your code). I told you I was stubborn... :-) Pretty sure you can now take it from here. -
Catttdaddy over 10 yearsThank you much! I assume "x" and "y" are reserved for position. I think I got it!
-
Catttdaddy over 10 yearsThis is why I am not using the "heading". I am asking how to determine the heading from 2 gps locations. Not how to use heading.