user2847238
user2847238

Reputation: 169

Determine compass direction

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.

enter image description here

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

Answers (1)

bajicdusko
bajicdusko

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

Related Questions