Calculate compass carrying / heading for location in Android

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

a) I calculated the north using the sensor values ​​from the magnetometer and accelerometer. I know this is correct because it matches the compass used in the Google Map view.

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

I miss the last step; of these two values ​​(a and b), which formula do I use to get the direction the phone is pointing relative to the destination?

Appreciate any help for a willful mind!

+53
android location bearing compass-geolocation
Nov 29 '10 at 21:25
source share
11 answers

Ok, I figured it out. For those trying to do this, you need:

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

b) bearing: bearing from your location to your destination. It is in degrees east of truth north.

myLocation.bearingTo(destLocation); 

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

The heading returned from the magnetometer + accelerator is in degrees east of the true (magnetic) north (-180 to +180), so you need to get the difference between north and magnetic north for your location. This difference varies depending on where you are on earth. You can get using the 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 them, you calculate the angle of the arrow to draw on the map to show where you are, in relation to the destination, and not in the north.

First set your headline with a slope:

 heading += geoField.getDeclination(); 

Secondly, you need to compensate for the direction in which the phone is looking (heading) from the destination, not north. This is the part I'm stuck on. The heading value returned from the compass gives you a value that describes where the magnetic north (in degrees east of true north) is relative to where the phone is pointing. So, for example, if the value is -10, you know that magnetic north is 10 degrees to your left. The bearing gives you the destination angle in degrees east of the true north. Therefore, after you have received compensation for the deviation, you can use the formula below to get the desired result:

 heading = myBearing - (myBearing + heading); 

Then you will want to convert degrees east of the true north (-180 to +180) to normal degrees (0 to 360):

 Math.round(-heading / 360 + 180) 
+64
Nov 30 '10 at 17:43
source share

@Damian. The idea is very good, and I agree with the answer, but when I used your code, I had the wrong values, so I wrote it myself (someone said the same in your comments). I think heading counting with declination is good, but later I used something like this:

 heading = (bearing - heading) * -1; 

instead of Damian code:

 heading = myBearing - (myBearing + heading); 

and change -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 turn the arrow, you can use a 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 an ImageView with an arrow image and 100f parameters in postRotate are pivX and pivY).

Hope I help someone.

+16
Mar 12 '13 at 15:02
source share

In this, the compass arrow shows the direction from your location to Kaaba ( destination )

You can just use bearingTo this way. You can get a right angle from your location to your destination.

  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 of -180 to 180, which is a bit confusing. We will need to convert this value to a range from 0 to 360 in order to get the correct rotation.

This is a table of what we really want, compared to what 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 implement SensorEventListener and its functions (onSensorChanged, onAcurracyChabge) and write all the code inside onSensorChanged

The full code is here for the Qibla compass direction

  public class QiblaDirectionCompass extends Service implements SensorEventListener{ public static ImageView image,arrow; // record the compass picture angle turned private float currentDegree = 0f; private float currentDegreeNeedle = 0f; Context context; Location userLoc=new Location("service Provider"); // device sensor manager private 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 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 capabilities this.context =context; @Override public 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(); } @Override public void onDestroy() { mSensorManager.unregisterListener(this); Toast.makeText(context, "Destroy", Toast.LENGTH_SHORT).show(); super.onDestroy(); } @Override public void onSensorChanged(SensorEvent sensorEvent) { 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); //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 north if (bearTo < 0) { bearTo = bearTo + 360; //bearTo = -100 + 360 = 260; } //This is where we choose to point it float 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 place ra.setDuration(210); // set the animation after the end of the reservation status ra.setFillAfter(true); // Start the animation image.startAnimation(ra); currentDegree = -degree; } @Override public void onAccuracyChanged(Sensor sensor, int i) { } @Nullable @Override public IBinder onBind(Intent intent) { return null; } 

xml code 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" /> <RelativeLayout android: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> 
+4
May 29 '17 at 19:14
source share

I am not an expert in the field of reading maps / navigation and so on, but, of course, the "directions" are absolute, not relative or in fact, they refer to N or S, which themselves are fixed / absolute.

Example. Suppose an imaginary line drawn between you and your destination corresponds to an “absolute” SE (bearing 135 degrees relative to magnetic N). Now suppose your phone points to NW - if you draw an imaginary line from an imaginary object on the horizon to the destination, it will go through your location and will have an angle of 180 degrees. Now 180 degrees in the sense of the compass actually refers to S, but the destination is not “due to S” the imaginary object that your phone points to, and, moreover, if you traveled to this imaginary point, your destination is - There will still be SE where you moved.

In fact, the 180 degree line actually tells you that the destination is “behind” with respect to the way the phone (and presumably you) is indicated.

Having said that, however, if calculating the angle of the line from the imaginary point to the destination (passing through your location) to draw a pointer to the destination, you want ... to simply subtract (absolute) carrying the target from absolute recognition of the imaginary object and ignoring the negation (if present). for example, NW - SE is 315 - 135 = 180, so draw a pointer so that it points at the bottom of the screen, indicating "behind you."

EDIT: I was a little wrong in the math ... subtract the smaller of the bearings from the larger, and then subtract the result from 360 to get the angle at which you want to draw a pointer on the screen.

+2
Nov 29 '10 at 23:41
source share

If you are in the same time zone

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 give you simple XY 2D

Calculate the angle between UTM locations

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

It gives direction as if you were looking north

So no matter what you rotate, make North just subtract that angle

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

+1
Jun 30 '11 at 17:08
source share

Here is how I did it:

 Canvas g = new Canvas( compass ); Paint p = new Paint( Paint.ANTI_ALIAS_FLAG ); float rotation = display.getOrientation() * 90; g.translate( -box.left, -box.top ); g.rotate( -bearing - rotation, box.exactCenterX(), box.exactCenterY() ); drawCompass( g, p ); drawNeedle( g, p ); 
+1
Sep 11 '11 at 16:32
source share

I know this is a little old, but for the sake of people like me from google who have not found a complete answer here. Here are some excerpts from my application that put arrows in a user list ...

 Location loc; //Will hold lastknown location Location wptLoc = new Location(""); // Waypoint location float dist = -1; float bearing = 0; float heading = 0; float arrow_rotation = 0; LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE); loc = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER); if(loc == null) { //No recent GPS fix Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); criteria.setAltitudeRequired(false); criteria.setBearingRequired(true); criteria.setCostAllowed(true); criteria.setSpeedRequired(false); loc = lm.getLastKnownLocation(lm.getBestProvider(criteria, true)); } if(loc != null) { wptLoc.setLongitude(cursor.getFloat(2)); //Cursor is from SimpleCursorAdapter wptLoc.setLatitude(cursor.getFloat(3)); dist = loc.distanceTo(wptLoc); bearing = loc.bearingTo(wptLoc); // -180 to 180 heading = loc.getBearing(); // 0 to 360 // *** Code to calculate where the arrow should point *** arrow_rotation = (360+((bearing + 360) % 360)-heading) % 360; } 

I bet it could be simplified, but it works! LastKnownLocation was used as this code was from the new SimpleCursorAdapter.ViewBinder ()

onLocationChanged contains a call to notifyDataSetChanged ();

also from the new SimpleCursorAdapter.ViewBinder () to set the rotation of the image and the colors of the list (applies to only one index column) ...

 LinearLayout ll = ((LinearLayout)view.getParent()); ll.setBackgroundColor(bc); int childcount = ll.getChildCount(); for (int i=0; i < childcount; i++){ View v = ll.getChildAt(i); if(v instanceof TextView) ((TextView)v).setTextColor(fc); if(v instanceof ImageView) { ImageView img = (ImageView)v; img.setImageResource(R.drawable.ic_arrow); Matrix matrix = new Matrix(); img.setScaleType(ScaleType.MATRIX); matrix.postRotate(arrow_rotation, img.getWidth()/2, img.getHeight()/2); img.setImageMatrix(matrix); } 

In case you are curious that I have done away with magnetic sensory dramas, in my case it was not worth the hassle. I hope someone finds this useful as usual when google brings me to stackoverflow!

+1
Nov 09 '14 at 8:54
source share

I understand this now, but it seems that the math depends on where you and your target are on the earth with respect to the 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 suggests that the declination is negative (west of the true north) and theirBearing> yourBearing.

If the declension is positive and yourBearing> theirBearing is another option:

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

I did not test it completely, but playing with corners on paper brought me here.

0
Aug 17 '12 at 23:52
source share

This is the best way to find Bearing from Location Object on a Google Map: →

  float targetBearing=90; Location endingLocation=new Location("ending point"); Location startingLocation=new Location("starting point"); startingLocation.setLatitude(mGoogleMap.getCameraPosition().target.latitude); startingLocation.setLongitude(mGoogleMap.getCameraPosition().target.longitude); endingLocation.setLatitude(mLatLng.latitude); endingLocation.setLongitude(mLatLng.longitude); targetBearing = startingLocation.bearingTo(endingLocation); 



0
Jun 13 '16 at 11:50
source share

The formula will provide support using the coordinates of the starting point to the ending point, see

The following code will give you support (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)); 

}

It works for me, hope it works, who finds the same

0
Sep 29 '16 at 16:22
source share

Terminology: The difference between TRUE North and Magnetic North is called “change”, not declination. The difference between reading your compass and a magnetic heading is called “deviation” and changes with the heading. The compass skew identifies device errors and allows corrections to be applied if the device has built-in correction. The magnetic compass will have a deviation map that describes the device error for any heading.

Declination: a term used in astrological navigation: declination is like latitude. He reports how far the star is from the celestial equator. To find the declination of a star, follow the straight-down hour circle from the star to the celestial equator. The angle from the star to the celestial equator in a circle of hours is the declination of the star.

-one
Aug 08 2018-12-12T00:
source share



All Articles