1

I would like to implement three things:

1) Implement a compass based on Location sensor + get current coordinates

2) Set bearing to fixed destination

3) Once bearing is set, I would like to set a red compass needle that will continue pointing to that fixed bearing regardless of phone rotation.

So far I have accomplished 1 + 2. As regards of 3 - I have managed to point the needle in the correct direction, but as phone rotates - the needle rotates too, loosing correct direction. I have tried many things and I just can't get this working.

One more question: If bearingTo() method calculates the degrees between two locations - does it take in account the earth curvature? If not, this only will be accurate for close distances... Hope it does :-)

I would appreciate any help.

Here is my code - check comments for explanation. Thanks in advance!

    @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

            tvCurrAzim = (TextView) findViewById(R.id.tvaz);
            tvCurrLat = (TextView) findViewById(R.id.tvlat);
            tvCurrLng = (TextView) findViewById(R.id.tvlng);
            tvBearing = (TextView) findViewById(R.id.tvbearing);
            tvIndicator = (TextView) findViewById(R.id.tvindicator);
            ivCompass = (ImageView) findViewById(R.id.imageView1); //compass ImageView
            ivArrow = (ImageView) findViewById(R.id.imageView2); //Red Needle ImageView

            // Get the location manager
            locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

            // Define the criteria how to select the location provider -> use
            // default
            Criteria criteria = new Criteria();
            provider = locationManager.getBestProvider(criteria, true);

            location = locationManager.getLastKnownLocation(provider);

            tvCurrLat.setText(String.valueOf(location.getLatitude()));
            tvCurrLng.setText(String.valueOf(location.getLongitude()));

            from = new Location(location); //creating new Location object
            to = new Location(location); //creating new Location object

    //setting lat,lng to destination Location
            to.setLatitude(newLat);
            to.setLongitude(newLng);

            mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
            mCompass = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);

        }

        // Compass Sensor Methods:

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onSensorChanged(SensorEvent event) {
            // setting animation for compass
            azimuth = Math.round(event.values[0]);

            tvCurrAzim.setText(Float.toString(azimuth));

            // get the angle around the z-axis rotated
            // float degree = Math.round(event.values[0]);

            // create a rotation animation (reverse turn degree degrees)
            RotateAnimation ra = new RotateAnimation(currentDegree, -azimuth,
                    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
            ivCompass.startAnimation(ra);
            currentDegree = -azimuth;

            pointArrow();

        }

        // Location Manager Methods:

        public void setBearing() {
            from.setLatitude(location.getLatitude()); //setting lat,lng to origin Location
            from.setLongitude(location.getLongitude());

            bearing = Math.round(from.bearingTo(to));
            tvBearing.setText(String.valueOf(bearing));
            hasBearing = true;
            ivArrow.setVisibility(View.VISIBLE);

        }


        public void pointArrow() {
            if (hasBearing) { //once bearing is set

                // get the angle around the z-axis rotated
                float degree1 = Math.round(azimuth) - (float) bearing;

                // create a rotation animation (reverse turn degree degrees)
                RotateAnimation ra1 = new RotateAnimation(currentDegree1, -degree1,
                        Animation.RELATIVE_TO_SELF, 0.5f,
                        Animation.RELATIVE_TO_SELF, 0.5f);

                // how long the animation will take place
                ra1.setDuration(210);

                // set the animation after the end of the reservation status
                ra1.setFillAfter(true);

                // Start the animation
                ivArrow.startAnimation(ra1);
                currentDegree1 = -degree1;

//THIS METHOD WORKS TO POINT THE NEEDLE IN CORRECT DIRECTION, BUT AS PHONE ROTATES THE NEEDLE ROTATES TOO.
//I WANT IT TO KEEP POINTING IN FIXED DIRECTION REGARDLESS OF PHONE ROTATION

            } 
        }

        @Override
        public void onLocationChanged(Location location) {
            tvCurrLat.setText(String.valueOf(location.getLatitude()));
            tvCurrLng.setText(String.valueOf(location.getLongitude()));
            setBearing();
            locationManager.removeUpdates(this);

        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

        }

        @Override
        protected void onPause() {
            // Unregister the listener
            super.onPause();
            locationManager.removeUpdates(this);
            mSensorManager.unregisterListener(this);
            ivArrow.setVisibility(View.INVISIBLE);

        }

        @Override
        protected void onResume() {
            super.onResume();
            locationManager.requestLocationUpdates(provider, 1000, 1, this);
            mSensorManager.registerListener(this, mCompass,
                    SensorManager.SENSOR_DELAY_GAME);
        }
gilonm
  • 1,829
  • 1
  • 12
  • 22

1 Answers1

0

Android docs says that bearingTo() uses WGS84 ellipsoid, so yes, it takes into consideration Earth curvature.

To make your compass be stable at device rotation using location sensor is not enough, you should also use magnetic field sensor. Most Android devices have them.

You need SensorManager.getOrientation() to look at: http://developer.android.com/reference/android/hardware/SensorManager.html

Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
  • Thanks for your answer.. Would you have an idea how to accomplish the third section I mentions in my question? Thanks! – gilonm Mar 28 '14 at 09:36
  • @gilonm in a textview tvCurrAzim, does text change when the device is being rotated? – Alex Salauyou Mar 28 '14 at 11:01
  • @gilonm if so, the problem is with redrawing the pointer, not obtaining device orientation... – Alex Salauyou Mar 28 '14 at 13:36
  • @gilonm to make your code more clear, readable and easy to test, concentrate all methods and properties related to pointing arrow in a separate class. It should store arrow's current rotation, angle, animation resources - so after receiving new orientation you just call pointing arrow object's update method and it makes all other work itself. – Alex Salauyou Mar 28 '14 at 13:46
  • @gilonm I've made such an object to handle orientation updates, have a look http://stackoverflow.com/questions/22725606/android-how-to-make-rotateanimation-more-smooth-and-physical – Alex Salauyou Mar 29 '14 at 01:39