Calculate compass bearing / heading to location in

2019-01-01 12:07发布

I want to display an arrow at my location on a google map view that displays my direction relative to a destination location (instead of north).

a) I have calculated north using the sensor values from the magnetometer and accelerometer. I know this is correct because it lines up with the compass used on the Google Map view.

b) I have calculated the initial bearing from my location to the destination location by using myLocation.bearingTo(destLocation);

I'm missing the last step; from these two values (a & b) what formula do I use to get the direction in which the phone is pointing relative to the destination location?

Appreciate any help for an addled mind!

12条回答
只若初见
2楼-- · 2019-01-01 12:44

Ok I figured this out. For anyone else trying to do this you need:

a) heading: your heading from the hardware compass. This is in degrees east of magnetic north

b) bearing: the bearing from your location to the destination location. This is in degrees east of true north.

myLocation.bearingTo(destLocation);

c) declination: the difference between true north and magnetic north

The heading that is returned from the magnetometer + accelermometer is in degrees east of true (magnetic) north (-180 to +180) so you need to get the difference between north and magnetic north for your location. This difference is variable depending where you are on earth. You can obtain by using GeomagneticField class.

GeomagneticField geoField;

private final LocationListener locationListener = new LocationListener() {
   public void onLocationChanged(Location location) {
      geoField = new GeomagneticField(
         Double.valueOf(location.getLatitude()).floatValue(),
         Double.valueOf(location.getLongitude()).floatValue(),
         Double.valueOf(location.getAltitude()).floatValue(),
         System.currentTimeMillis()
      );
      ...
   }
}

Armed with these you calculate the angle of the arrow to draw on your map to show where you are facing in relation to your destination object rather than true north.

First adjust your heading with the declination:

heading += geoField.getDeclination();

Second, you need to offset the direction in which the phone is facing (heading) from the target destination rather than true north. This is the part that I got stuck on. The heading value returned from the compass gives you a value that describes where magnetic north is (in degrees east of true north) in relation to where the phone is pointing. So e.g. if the value is -10 you know that magnetic north is 10 degrees to your left. The bearing gives you the angle of your destination in degrees east of true north. So after you've compensated for the declination you can use the formula below to get the desired result:

heading = myBearing - (myBearing + heading); 

You'll then want to convert from degrees east of true north (-180 to +180) into normal degrees (0 to 360):

Math.round(-heading / 360 + 180)
查看更多
有味是清欢
3楼-- · 2019-01-01 12:47

If you are on the same timezone

Convert GPS to UTM

http://www.ibm.com/developerworks/java/library/j-coordconvert/ http://stackoverflow.com/questions/176137/java-convert-lat-lon-to-utm

UTM coordinates get you a simples X Y 2D

Calculate the angle between both UTM locations

http://forums.groundspeak.com/GC/index.php?showtopic=146917

This gives the direction as if you were looking north

So whatever you rotate related do North just subtract this angle

If both point have a UTM 45º degree angle and you are 5º east of north, your arrow will point to 40º of north

查看更多
千与千寻千般痛.
4楼-- · 2019-01-01 12:50

The formula will give the bearing using the coordinates of the start point to the end point see

The following code will give you the bearing (angle between 0-360)

private double bearing(Location startPoint, Location endPoint) {
    double longitude1 = startPoint.getLongitude();
    double latitude1 = Math.toRadians(startPoint.getLatitude());

    double longitude2 = endPoint.getLongitude();        
    double latitude2 = Math.toRadians(endPoint.getLatitude());

    double longDiff = Math.toRadians(longitude2 - longitude1);

    double y = Math.sin(longDiff) * Math.cos(latitude2);
    double x = Math.cos(latitude1) * Math.sin(latitude2) - Math.sin(latitude1) * Math.cos(latitude2) * Math.cos(longDiff);

    return Math.toDegrees(Math.atan2(y, x));

}

This works for me hope it will work others as well

查看更多
弹指情弦暗扣
5楼-- · 2019-01-01 12:51

I'm no expert in map-reading / navigation and so on but surely 'directions' are absolute and not relative or in reality, they are relative to N or S which themselves are fixed/absolute.

Example: Suppose an imaginary line drawn between you and your destination corresponds with 'absolute' SE (a bearing of 135 degrees relative to magnetic N). Now suppose your phone is pointing NW - if you draw an imaginary line from an imaginary object on the horizon to your destination, it will pass through your location and have an angle of 180 degrees. Now 180 degrees in the sense of a compass actually refers to S but the destination is not 'due S' of the imaginary object your phone is pointing at and, moreover, if you travelled to that imaginary point, your destination would still be SE of where you moved to.

In reality, the 180 degree line actually tells you the destination is 'behind you' relative to the way the phone (and presumably you) are pointing.

Having said that, however, if calculating the angle of a line from the imaginary point to your destination (passing through your location) in order to draw a pointer towards your destination is what you want...simply subtract the (absolute) bearing of the destination from the absolute bearing of the imaginary object and ignore a negation (if present). e.g., NW - SE is 315 - 135 = 180 so draw the pointer to point at the bottom of the screen indicating 'behind you'.

EDIT: I got the Maths slightly wrong...subtract the smaller of the bearings from the larger then subtract the result from 360 to get the angle in which to draw the pointer on the screen.

查看更多
余生请多指教
6楼-- · 2019-01-01 12:59

Terminology : The difference between TRUE north and Magnetic North is known as "variation" not declination. The difference between what your compass reads and the magnetic heading is known as "deviation" and varies with heading. A compass swing identifies device errors and allows corrections to be applied if the device has correction built in. A magnetic compass will have a deviation card which describes the device error on any heading.

Declination : A term used in Astro navigation : Declination is like latitude. It reports how far a star is from the celestial equator. To find the declination of a star follow an hour circle "straight down" from the star to the celestial equator. The angle from the star to the celestial equator along the hour circle is the star's declination.

查看更多
明月照影归
7楼-- · 2019-01-01 12:59

I am in the process of figuring it out now but it seems as though the math depends on where you and your target are on the earth relative to true and magnetic North. For example:

float thetaMeThem = 0.0;
if (myLocation.bearingTo(targetLocation) > myLocation.getBearing()){ 
     thetaMeThem = myLocation.bearingTo(targetLocation) - azimuth + declination;} 

See Sensor.TYPE_ORIENTATION for azimuth.

See getDeclination() for declination

This assumes declination is negative (west of true north) and theirBearing > yourBearing.

If declination is positive and yourBearing > theirBearing another option:

float thetaMeThem = 0.0;
if (myLocation.bearingTo(targetLocation) < myLocation.getBearing()){ 
     thetaMeThem = azimuth - (myLocation.bearingTo(targetLocation) - declination);} 

I haven't tested this fully but playing with the angles on paper got me here.

查看更多
登录 后发表回答