How can I determine direction with GPS readings alone and display in 360 degrees?
Asked Answered
M

3

7

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;

            }            
        }
    }
}
Marine answered 29/8, 2013 at 20:43 Comment(3)
If your updates are sufficiently frequent that both lat or lon didn't change, you will end up with an error (that probably results in zero). Depending on the exact implementation, it is possible that the value is reported to relatively few sig figs (if accuracy is low), in which case the difference may well be zero. What happens if you print the four values lat1, lat2, lon1, lon2 before computing the bearing? Or just give the full text of my_txt.text?Parbuckle
Its being generated before bearing. Its the same either way. I get "Latitude is 42.XXXXX84 and my Longitude is -83.XXXXX77 Bearing is 0 HorAcc 129 VertAcc 129 PreLat 42.XXXXX83 PreLon -83.XXXXX10" this is from walking around my office a bit.Marine
I think now you can use: public float getBearing () Added in API level 1 Get the bearing, in degrees. Bearing is the horizontal direction of travel of this device, and is not related to the device orientation. It is guaranteed to be in the range (0.0, 360.0] if the device has a bearing. If this location does not have a bearing then 0.0 is returned.Reed
P
7

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).

Parbuckle answered 29/8, 2013 at 20:55 Comment(24)
I think I got the code implemented. It all looks correct but all I am getting is a zero value.Marine
Could 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 and y?Parbuckle
It all looks correct... except that the units of longitude and latitude probably don't "play nice" with your current use of the sin and cos functions. The former are presumably in degrees, and the latter expect radians. You convert degrees to radians with rads=degs*Math.PI/180; sometimes, a function sind and cosd is built in which expects the argument in degrees. I would print out x and y to help in the debugging, for sure.Parbuckle
Im getting LonR=-1.45xxxxxxxxxxxx79 and LatR=0.73xxxxxxxxxxx84Marine
Im 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".Marine
Sorry yes that was a typo. What do you get for the bearing when you fix the typo?Parbuckle
The bearing is still "0"Marine
How strange. What do you get for x and y? Does your platform even recognize the atan2 function? In some systems the parameters for atan2 are reversed - meaning that you need x,y instead of y,x in order to get the right answer in those. You could also try using atan2(y/x, 1) (which works if x != 0) to see if rounding error in the atan2 function is the problem. What do you get for atan2(1,1) and atan2(1e-10, 1e-14), for example? The answer should be pi/4 and approximately pi/2, respectively...Parbuckle
Looking 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 your onGeoUpdate function, doesn't it?Parbuckle
X 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...Marine
Not sure I understand your last sentence. "I thought about...". what did you conclude? You need to compute bearing each time the location updates. Add gpsHeading(); right before gpsHeading = (bearing);. And don't use gpsHeading = (e.heading); in the gpsCheck == 2 case: it returns NaN on Android.Parbuckle
Sorry 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.Marine
I'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?Parbuckle
From 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 HereMarine
It looks like changing dLon = gpsLon1R - gpsLon2R; too dLon = 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.Marine
It was helpful that you created the wonderfl example. It turns out that variables x and y 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 the geoUpdate() function out to inline everything (because I was confused about the scope of x and y in your code). I told you I was stubborn... :-) Pretty sure you can now take it from here.Parbuckle
Thank you much! I assume "x" and "y" are reserved for position. I think I got it!Marine
It seems to work but I get negative numbers... Im not sure why. The negative numbers are throwing my display of the direction off. I have a movie clip called compassMov with 359 frames for each degree. when it goes negative it throws the whole thing off...Marine
Of course. The range of atan2 is from -pi to pi - you need to take the answer modulo 360 once you are in degrees. Add bearing = mod(bearing, 360); and all will be wellParbuckle
Is that bearing = mod(bearing, 360); the same as bearing = (bearing %= 360); bearing = mod(bearing, 360) doesn't compileMarine
That's what I meant. Sorry had been answering a similar question in a different language... Should have checked more carefully. But yes atan2 has a range from -pi to pi so needs forcing I to range. You could also use if (bearing<0) bearing+=360; - as I meantioned earlier in the answer.Parbuckle
Thanks for the help but one thing is wrong. I know its something I may be overlooking. The East and west degrees are backwards. meaning when I am going West I get 90 degrees. When I am going east I get 270... After this it should work perfect. I am not familiar enough with the math here to identify the issue...Marine
So North is North, South is South - but you run into a problem with East and West being switched? I conjecture that somehow the sign of dlonr must be backwards. Perhaps it should have been lon1r - lon2r. A bit too tired to figure this out right now. See if that fixes it?Parbuckle
I think now you can use: public float getBearing () Added in API level 1 Get the bearing, in degrees. Bearing is the horizontal direction of travel of this device, and is not related to the device orientation. It is guaranteed to be in the range (0.0, 360.0] if the device has a bearing. If this location does not have a bearing then 0.0 is returned.Reed
C
3

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

Cooncan answered 29/8, 2013 at 20:47 Comment(7)
When 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?Marine
the 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 is location.getBearing() in the callback when your location changesCooncan
I 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"...Marine
why do you need to use the AIR API for location just use the android API, it will probably solve most of your problemsCooncan
Because I am familiar with Actionscript 3 and not Java.Marine
From 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)."Parbuckle
This 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.Marine
S
0

I used a "breadcrumb" solution.

I addchild mc to my current location. As I move the new mc is compared to the previous mc.

so first i find my coordinates on a fixed map, then locate my "you are here". Then lay breadcrumbs as I move... then compare those breadcrumbs to find heading.

// geolocation
function onGeoUpdate(event:GeolocationEvent):void{

MapHolder.Star.x = MapHolder.Map.x + (event.longitude - (Min_Longitude)) / ((Max_Longitude) - (Min_Longitude)) * 940;
MapHolder.Star.y = MapHolder.Map.y + 800 - (event.latitude - Min_Latitude) / (Max_Latitude - Min_Latitude) * 800;

/// then create new and previous
var  MapHolderStarxold = MapHolder.Star.x;
var  MapHolderStaryold = MapHolder.Star.y;

///breadcrumbs
        var randomMc:Number = Math.floor(Math.random()*myArray.length);
        var mc:MovieClip =  new myArray[randomMc];
        MapHolder.addChild(mc);

         mc.x = MapHolder.Star.x;
         mc.y = MapHolder.Star.y;

         MapHolder.dirArrow.x = MapHolderStarxold;
         MapHolder.dirArrow.y = MapHolderStaryold;


   //// MapHolder.Star point   
        // find out mouse coordinates to find out the angle
var cyB:Number = mc.y - MapHolder.dirArrow.y; 
var cxB:Number = mc.x - MapHolder.dirArrow.x;
// find out the angle
var RadiansB:Number = Math.atan2(cyB,cxB);
// convert to degrees to rotate
var DegreesB:Number = RadiansB * 180 / Math.PI;
// rotate
MapHolder.dirArrow.rotation = DegreesB; }
Santosantonica answered 21/10, 2013 at 17:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.