Reputation: 169
I am browsing the internet trying to find something that would be usefull in my Android App. I have my GPS location and several markers added to Google Map. When I turn around the location arrow is ponting wherever I am facing. My concern is if there is any way to determine on which marker I am pointing at? I have made a picture to clarify this.
I presume I can get compass bearing using getRotationMatrix() method, but how can I determine angle between location and the marker?
Upvotes: 0
Views: 8241
Reputation: 1650
Checkout Compass class in this project: https://github.com/iutinvg/compass
I have used it succsesfully in this app: https://play.google.com/store/apps/details?id=com.gps.build
Regards.
UPDATE:
After reading your question again, i have realized that I did not give you enough details for "pointing to marker" part. Please check full class below. To calculate pointing direction, us startBearing and stopBearing methods.
Note that bearing degrees are changing with device rotation in BringMeBack class with setBearingDegrees method. Thats is not what you need, so you just have to remove locationManager and to put static bearing coordinate. And call that method only once.
Extended compass class:
package com.gps.bitlab;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import com.gps.bitlab.fragment.MessageDialogFragment;
import com.gps.bitlab.util.Utility;
public class Compass implements SensorEventListener {
private static final String TAG = "Compass";
private SensorManager sensorManager;
private Sensor gsensor;
private Sensor msensor;
private float[] mGravity = new float[3];
private float[] mGeomagnetic = new float[3];
private float azimuth = 0f;
private float currectAzimuth = 0;
private boolean bearing = false;
private float bearingDegrees = -1;
// compass arrow to rotate
public ImageView arrowView = null;
FragmentActivity activity;
public Compass(FragmentActivity activity) {
this.activity = activity;
sensorManager = (SensorManager) activity.getApplicationContext()
.getSystemService(Context.SENSOR_SERVICE);
gsensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
msensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
public void start() {
boolean deviceSensorCompatible = true;
if(!sensorManager.registerListener(this, gsensor, SensorManager.SENSOR_DELAY_GAME))
deviceSensorCompatible = false;
if(!sensorManager.registerListener(this, msensor, SensorManager.SENSOR_DELAY_GAME))
deviceSensorCompatible = false;
if(!deviceSensorCompatible) {
Utility.ShowMessage(activity, activity.getString(R.string.erroroccured), activity.getString(R.string.deviceIncompatible), 1);
stop();
}
}
public void startBearing()
{
bearing = true;
start();
}
public void setBearingDegrees(float bearingDegrees)
{
this.bearingDegrees = bearingDegrees;
}
public void stop() {
sensorManager.unregisterListener(this);
}
public void stopBearing()
{
bearing = false;
stop();
}
private void adjustArrow() {
if (arrowView == null) {
Log.i(TAG, "arrow view is not set");
return;
}
Animation an = new RotateAnimation(-currectAzimuth, -azimuth,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
currectAzimuth = azimuth;
an.setDuration(250);
an.setRepeatCount(0);
an.setFillAfter(true);
arrowView.startAnimation(an);
}
@Override
public void onSensorChanged(SensorEvent event) {
final float alpha = 0.97f;
synchronized (this) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mGravity[0] = alpha * mGravity[0] + (1 - alpha)
* event.values[0];
mGravity[1] = alpha * mGravity[1] + (1 - alpha)
* event.values[1];
mGravity[2] = alpha * mGravity[2] + (1 - alpha)
* event.values[2];
// mGravity = event.values;
// Log.e(TAG, Float.toString(mGravity[0]));
}
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
// mGeomagnetic = event.values;
mGeomagnetic[0] = alpha * mGeomagnetic[0] + (1 - alpha)
* event.values[0];
mGeomagnetic[1] = alpha * mGeomagnetic[1] + (1 - alpha)
* event.values[1];
mGeomagnetic[2] = alpha * mGeomagnetic[2] + (1 - alpha)
* event.values[2];
// Log.e(TAG, Float.toString(event.values[0]));
}
float R[] = new float[9];
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, mGravity,
mGeomagnetic);
if (success) {
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);
// Log.d(TAG, "azimuth (rad): " + azimuth);
azimuth = (float) Math.toDegrees(orientation[0]); // orientation
azimuth = (azimuth + 360) % 360;
if(bearing) {
if(bearingDegrees != -1) {
azimuth -= bearingDegrees;
adjustArrow();
}
}
else
adjustArrow();
// Log.d(TAG, "azimuth (deg): " + azimuth);
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
Class that use pointing to location functionality:
package com.gps.bitlab;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.gps.bitlab.R;
import com.gps.bitlab.fragment.OnDialogClickListener;
import com.gps.bitlab.util.Utility;
public class BringMeBack extends ActionBarActivity implements LocationListener, OnDialogClickListener {
LocationManager locMng;
Location location;
double lat;
double lon;
double alt;
String name;
Compass compass;
FrameLayout bearingParentLayout;
LinearLayout BaseLayout;
ImageView arrow;
boolean layoutReplaced = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Utility.SetLocalization(this);
setContentView(R.layout.activity_bring_me_back);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getActionBar().setDisplayHomeAsUpEnabled(true);
bearingParentLayout = (FrameLayout)findViewById(R.id.bearingParentLayout);
BaseLayout = Utility.GetLoadingView(getLayoutInflater(), getString(R.string.waitingForLocation));
if(savedInstanceState != null)
{
lat = savedInstanceState.getDouble("lat");
lon = savedInstanceState.getDouble("lon");
alt = savedInstanceState.getDouble("alt");
name = savedInstanceState.getString("name");
}
else {
lat = getIntent().getExtras().getDouble("lat");
lon = getIntent().getExtras().getDouble("lon");
alt = getIntent().getExtras().getDouble("alt");
name = getIntent().getExtras().getString("name");
}
if(name != null && !name.equals(""))
getActionBar().setTitle(name);
locMng = (LocationManager)getSystemService(LOCATION_SERVICE);
location = new Location(LocationManager.GPS_PROVIDER);
location.setLongitude(lon);
location.setLatitude(lat);
location.setAltitude(alt);
arrow = (ImageView)findViewById(R.id.bearingArrow);
compass = new Compass(this);
compass.arrowView = arrow;
arrow.setVisibility(View.GONE);
bearingParentLayout.addView(BaseLayout);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == android.R.id.home) {
BringMeBack.this.finish();
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onPause() {
super.onPause();
compass.stopBearing();
locMng.removeUpdates(this);
}
@Override
protected void onResume() {
super.onResume();
compass.startBearing();
RequestLocationUpdates();
}
@Override
public void onLocationChanged(Location currentLocation) {
float bearing = currentLocation.bearingTo(location);
Log.d("Location bearing", String.valueOf(bearing));
compass.setBearingDegrees(bearing);
if(!layoutReplaced) {
bearingParentLayout.removeView(BaseLayout);
arrow.setVisibility(View.VISIBLE);
layoutReplaced = true;
}
}
@Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
@Override
public void onProviderEnabled(String s) {
Log.d("GPS", "Service enabled");
RequestLocationUpdates();
}
@Override
public void onProviderDisabled(String s) {
locMng.removeUpdates(this);
Utility.ShowMessage(BringMeBack.this, getString(R.string.locationServiceDisabledMessage), getString(R.string.locationServiceDisabled), 0);
}
@Override
public void OnPositiveClick(int key, Object... args) {
startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
}
@Override
public void OnNegativeClick(int key, Object... args) {
}
private void RequestLocationUpdates()
{
if(!locMng.isProviderEnabled(LocationManager.GPS_PROVIDER))
Utility.ShowMessage(BringMeBack.this, getString(R.string.locationServiceDisabledMessage), getString(R.string.locationServiceDisabled), 0);
else
locMng.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 0, this);
}
}
Upvotes: 8