Calculate compass bearing / heading to location in Android Calculate compass bearing / heading to location in Android android android

Calculate compass bearing / heading to location in Android


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)


@Damian - The idea is very good and I agree with answer, but when I used your code I had wrong values, so I wrote this on my own (somebody told the same in your comments). Counting heading with the declination is good, I think, but later I used something like that:

heading = (bearing - heading) * -1;

instead of Damian's code:

heading = myBearing - (myBearing + heading); 

and changing -180 to 180 for 0 to 360:

      private float normalizeDegree(float value){          if(value >= 0.0f && value <= 180.0f){              return value;          }else{              return 180 + (180 + value);          }

and then when you want to rotate your arrow you can use code like this:

      private void rotateArrow(float angle){            Matrix matrix = new Matrix();            arrowView.setScaleType(ScaleType.MATRIX);            matrix.postRotate(angle, 100f, 100f);            arrowView.setImageMatrix(matrix);      }

where arrowView is ImageView with arrow picture and 100f parameters in postRotate is pivX and pivY).

I hope I will help somebody.


In this an arrow on compass shows the direction from your location to Kaaba(destination Location)

you can simple use bearingTo in this way.bearing to will give you the direct angle from your location to destination location

  Location userLoc=new Location("service Provider");    //get longitudeM Latitude and altitude of current location with gps class and  set in userLoc    userLoc.setLongitude(longitude);     userLoc.setLatitude(latitude);    userLoc.setAltitude(altitude);   Location destinationLoc = new Location("service Provider");  destinationLoc.setLatitude(21.422487); //kaaba latitude setting  destinationLoc.setLongitude(39.826206); //kaaba longitude setting  float bearTo=userLoc.bearingTo(destinationLoc);

bearingTo will give you a range from -180 to 180, which will confuse things a bit. We will need to convert this value into a range from 0 to 360 to get the correct rotation.

This is a table of what we really want, comparing to what bearingTo gives us

+-----------+--------------+| bearingTo | Real bearing |+-----------+--------------+| 0         | 0            |+-----------+--------------+| 90        | 90           |+-----------+--------------+| 180       | 180          |+-----------+--------------+| -90       | 270          |+-----------+--------------+| -135      | 225          |+-----------+--------------+| -180      | 180          |+-----------+--------------+

so we have to add this code after bearTo

// If the bearTo is smaller than 0, add 360 to get the rotation clockwise.  if (bearTo < 0) {    bearTo = bearTo + 360;    //bearTo = -100 + 360  = 260;}

you need to implements the SensorEventListener and its functions(onSensorChanged,onAcurracyChabge) and write all the code inside onSensorChanged

Complete code is here for Direction of Qibla compass

 public class QiblaDirectionCompass extends Service implements SensorEventListener{ public static ImageView image,arrow;// record the compass picture angle turnedprivate float currentDegree = 0f;private float currentDegreeNeedle = 0f;Context context;Location userLoc=new Location("service Provider");// device sensor managerprivate static SensorManager mSensorManager ;private Sensor sensor;public static TextView tvHeading;   public QiblaDirectionCompass(Context context, ImageView compass, ImageView needle,TextView heading, double longi,double lati,double alti ) {    image = compass;    arrow = needle;    // TextView that will tell the user what degree is he heading    tvHeading = heading;    userLoc.setLongitude(longi);    userLoc.setLatitude(lati);    userLoc.setAltitude(alti);  mSensorManager =  (SensorManager) context.getSystemService(SENSOR_SERVICE);    sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);    if(sensor!=null) {        // for the system's orientation sensor registered listeners        mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME);//SensorManager.SENSOR_DELAY_Fastest    }else{        Toast.makeText(context,"Not Supported", Toast.LENGTH_SHORT).show();    }    // initialize your android device sensor capabilitiesthis.context =context;@Overridepublic void onCreate() {    // TODO Auto-generated method stub    Toast.makeText(context, "Started", Toast.LENGTH_SHORT).show();    mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME); //SensorManager.SENSOR_DELAY_Fastest    super.onCreate();}@Overridepublic void onDestroy() {    mSensorManager.unregisterListener(this);Toast.makeText(context, "Destroy", Toast.LENGTH_SHORT).show();    super.onDestroy();}@Overridepublic void onSensorChanged(SensorEvent sensorEvent) {Location destinationLoc = new Location("service Provider");destinationLoc.setLatitude(21.422487); //kaaba latitude settingdestinationLoc.setLongitude(39.826206); //kaaba longitude settingfloat bearTo=userLoc.bearingTo(destinationLoc);  //bearTo = The angle from true north to the destination location from the point we're your currently standing.(asal image k N se destination taak angle )  //head = The angle that you've rotated your phone from true north. (jaise image lagi hai wo true north per hai ab phone jitne rotate yani jitna image ka n change hai us ka angle hai ye)GeomagneticField geoField = new GeomagneticField( Double.valueOf( userLoc.getLatitude() ).floatValue(), Double        .valueOf( userLoc.getLongitude() ).floatValue(),        Double.valueOf( userLoc.getAltitude() ).floatValue(),        System.currentTimeMillis() );head -= geoField.getDeclination(); // converts magnetic north into true northif (bearTo < 0) {    bearTo = bearTo + 360;    //bearTo = -100 + 360  = 260;}//This is where we choose to point itfloat direction = bearTo - head;// If the direction is smaller than 0, add 360 to get the rotation clockwise.if (direction < 0) {    direction = direction + 360;} tvHeading.setText("Heading: " + Float.toString(degree) + " degrees" );RotateAnimation raQibla = new RotateAnimation(currentDegreeNeedle, direction, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);raQibla.setDuration(210);raQibla.setFillAfter(true);arrow.startAnimation(raQibla);currentDegreeNeedle = direction;// create a rotation animation (reverse turn degree degrees)RotateAnimation ra = new RotateAnimation(currentDegree, -degree, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);// how long the animation will take placera.setDuration(210);// set the animation after the end of the reservation statusra.setFillAfter(true);// Start the animationimage.startAnimation(ra);currentDegree = -degree;}@Overridepublic void onAccuracyChanged(Sensor sensor, int i) {}@Nullable@Overridepublic IBinder onBind(Intent intent) {    return null;}

xml code is here

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/flag_pakistan"><TextView    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:id="@+id/heading"    android:textColor="@color/colorAccent"    android:layout_centerHorizontal="true"    android:layout_marginBottom="100dp"    android:layout_marginTop="20dp"    android:text="Heading: 0.0" /><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@+id/heading"android:scaleType="centerInside"android:layout_centerVertical="true"android:layout_centerHorizontal="true"><ImageView    android:id="@+id/imageCompass"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:scaleType="centerInside"    android:layout_centerVertical="true"    android:layout_centerHorizontal="true"    android:src="@drawable/images_compass"/><ImageView    android:id="@+id/needle"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_centerVertical="true"    android:layout_centerHorizontal="true"    android:scaleType="centerInside"    android:src="@drawable/arrow2"/></RelativeLayout></RelativeLayout>