diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java new file mode 100644 index 000000000..ce01e7b4c --- /dev/null +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java @@ -0,0 +1,29 @@ +package com.mapbox.mapboxsdk.constants; + +import android.support.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * MyBearingTracking exposes different types bearing tracking modes. + * + * @see + * @see + */ +public class MyBearingTracking { + + /** + * Indicates the parameter accepts one of the values from {@link MyBearingTracking}. + */ + @IntDef({NONE, COMPASS, GPS, /**COMBINED**/}) + @Retention(RetentionPolicy.SOURCE) + public @interface Mode { + } + + public static final int NONE = 0x00000000; + public static final int COMPASS = 0x00000004; + public static final int GPS = 0x00000008; +// public static final int COMBINED = 0x00000012; + +} diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java index 6cfc48c0e..f477c4ab4 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java @@ -8,7 +8,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * MyLocationTracking exposes different types of location tracking modes. + * MyLocationTracking exposes different types of locational tracking modes. * * @see MapView#setMyLocationTrackingMode(int) * @see com.mapbox.mapboxsdk.views.UserLocationView#setMyLocationTrackingMode(int) @@ -18,7 +18,7 @@ public class MyLocationTracking { /** * Indicates the parameter accepts one of the values from {@link MyLocationTracking}. */ - @IntDef({TRACKING_NONE, TRACKING_FOLLOW, /**TRACKING_FOLLOW_BEARING_GPS, TRACKING_FOLLOW_BEARING_COMPASS, TRACKING_FOLLOW_BEARING**/}) + @IntDef({TRACKING_NONE, TRACKING_FOLLOW}) @Retention(RetentionPolicy.SOURCE) public @interface Mode { } @@ -29,25 +29,8 @@ public class MyLocationTracking { public static final int TRACKING_NONE = 0x00000000; /** - * Tracking the only the location of the user. + * Tracking user, {@link MapView} will reposition to center of {@link com.mapbox.mapboxsdk.views.UserLocationView} */ public static final int TRACKING_FOLLOW = 0x00000004; -// /** -// * Tracking the location of the user with bearing from GPS. -// */ -// public static final int TRACKING_FOLLOW_BEARING_GPS = 0x00000008; -// -// /** -// * Tracking the location of the user with bearing from compass. -// */ -// public static final int TRACKING_FOLLOW_BEARING_COMPASS = 0x00000012; -// -// /** -// * Tracking the location fot the user with bearing from GPS or compass. -// *
-// * The best source of bearing is selected automatically. -// */ -// public static final int TRACKING_FOLLOW_BEARING = 0x00000016; - } \ No newline at end of file diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java index 3f865bea4..2b6a3071e 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/MapView.java @@ -64,6 +64,7 @@ import com.mapbox.mapboxsdk.annotations.Polyline; import com.mapbox.mapboxsdk.annotations.PolylineOptions; import com.mapbox.mapboxsdk.annotations.Sprite; import com.mapbox.mapboxsdk.annotations.SpriteFactory; +import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.exceptions.InvalidAccessTokenException; @@ -889,6 +890,7 @@ public final class MapView extends FrameLayout { */ @UiThread public void onStart() { + mUserLocationView.onStart(); } /** @@ -896,7 +898,7 @@ public final class MapView extends FrameLayout { */ @UiThread public void onStop() { - mUserLocationView.cancelAnimations(); + mUserLocationView.onStop(); } /** @@ -2182,7 +2184,14 @@ public final class MapView extends FrameLayout { // Used by UserLocationView void update() { - mNativeMapView.update(); + if (mNativeMapView != null) { + mNativeMapView.update(); + } + } + + // Used by UserLocationView + void setBearing(float bearing) { + mNativeMapView.setBearing(bearing, 100); } // @@ -3151,14 +3160,13 @@ public final class MapView extends FrameLayout { } /** - * Sets the current my location tracking mode. + * Set the current my location tracking mode. + * Tracking my location disables gestures and pans the viewport *
- * My location racking disables gestures, automatically moves the viewport to the users - * location and shows the direction the user is heading. + * See {@link MyLocationTracking} for different values. * - * @param myLocationTrackingMode The my location tracking mode to use. - * Accepts one of the values from {@link MyLocationTracking.Mode}. - * @see MyLocationTracking.Mode + * @param myLocationTrackingMode The location tracking mode to be used. + * @see MyLocationTracking */ @UiThread public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { @@ -3187,6 +3195,33 @@ public final class MapView extends FrameLayout { return mUserLocationView.getMyLocationTrackingMode(); } + /** + * Set the current my bearing tracking mode. + * Tracking my bearing disables gestures and shows the direction the user is heading. + * See {@link MyBearingTracking} for different values. + * + * @param myBearingTrackingMode The bearing tracking mode to be used. + * @see MyBearingTracking + */ + @UiThread + public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { + mUserLocationView.setMyBearingTrackingMode(myBearingTrackingMode); + } + + /** + * Returns the current user bearing tracking mode. + * See {@link MyBearingTracking} for possible return values. + * + * @return the current user bearing tracking mode. + * @see MyBearingTracking + */ + @UiThread + @MyLocationTracking.Mode + public int getMyBearingTrackingMode() { + //noinspection ResourceType + return mUserLocationView.getMyBearingTrackingMode(); + } + // // Compass // diff --git a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java index b2e93d532..349765bdd 100644 --- a/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java +++ b/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/views/UserLocationView.java @@ -12,16 +12,24 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; +import android.hardware.GeomagneticField; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.location.Location; import android.os.Build; import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; +import android.view.Surface; import android.view.View; import android.view.ViewGroup; +import android.view.WindowManager; import com.mapbox.mapboxsdk.R; +import com.mapbox.mapboxsdk.constants.MyBearingTracking; import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapzen.android.lost.api.LocationListener; @@ -29,6 +37,8 @@ import com.mapzen.android.lost.api.LocationRequest; import com.mapzen.android.lost.api.LocationServices; import com.mapzen.android.lost.api.LostApiClient; +import java.util.ArrayDeque; + final class UserLocationView extends View { private MapView mMapView; @@ -83,6 +93,12 @@ final class UserLocationView extends View { @MyLocationTracking.Mode private int mMyLocationTrackingMode; + @MyBearingTracking.Mode + private int mMyBearingTrackingMode; + + // Compass data + private MyBearingListener mBearingChangeListener; + public UserLocationView(Context context) { super(context); initialize(context); @@ -116,6 +132,9 @@ final class UserLocationView extends View { .setSmallestDisplacement(3.0f) .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + // Setup sensors + mBearingChangeListener = new MyBearingListener(context); + // Setup the custom paint Resources resources = context.getResources(); mDensity = resources.getDisplayMetrics().density; @@ -183,6 +202,17 @@ final class UserLocationView extends View { mMapView = mapView; } + public void onStart() { + if (mMyBearingTrackingMode == MyBearingTracking.COMPASS) { + mBearingChangeListener.onStart(getContext()); + } + } + + public void onStop() { + mBearingChangeListener.onStop(); + cancelAnimations(); + } + @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -321,6 +351,136 @@ final class UserLocationView extends View { } } + public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { + mMyBearingTrackingMode = myBearingTrackingMode; + + if (myBearingTrackingMode == MyBearingTracking.COMPASS) { + mBearingChangeListener.onStart(getContext()); + } else { + mBearingChangeListener.onStop(); + } + } + + @MyBearingTracking.Mode + public int getMyBearingTrackingMode() { + return mMyBearingTrackingMode; + } + + private class MyBearingListener implements SensorEventListener { + + // Sensor model + private SensorManager mSensorManager; + private Sensor mSensorRotationVector; + private int mRotationDevice; + + // Sensor data sensor rotation vector + private float[] mRotationMatrix = new float[16]; + private float[] mRemappedMatrix = new float[16]; + private float[] mOrientation = new float[3]; + + // Location data + private GeomagneticField mGeomagneticField; + + // Controls the sensor update rate in milliseconds + private static final int UPDATE_RATE_MS = 500; + private AngleLowPassFilter mLowPassFilter; + + // Compass data + private float mCompassBearing; + private long mCompassUpdateNextTimestamp = 0; + + public MyBearingListener(Context context) { + mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + mSensorRotationVector = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); + mLowPassFilter = new AngleLowPassFilter(); + } + + public void onStart(Context context) { + mRotationDevice = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); + mSensorManager.registerListener(this, mSensorRotationVector, UPDATE_RATE_MS * 1000); + } + + public void onStop() { + mSensorManager.unregisterListener(this, mSensorRotationVector); + } + + public float getCompassBearing() { + return mCompassBearing; + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (mPaused) { + return; + } + + switch (event.sensor.getType()) { + case Sensor.TYPE_ROTATION_VECTOR: + SensorManager.getRotationMatrixFromVector(mRotationMatrix, event.values); + break; + } + + switch (mRotationDevice) { + case Surface.ROTATION_0: + // Portrait + SensorManager.getOrientation(mRotationMatrix, mOrientation); + break; + default: + // Landscape + SensorManager.remapCoordinateSystem(mRotationMatrix, SensorManager.AXIS_MINUS_Y, SensorManager.AXIS_MINUS_X, mRemappedMatrix); + SensorManager.getOrientation(mRemappedMatrix, mOrientation); + break; + } + + mLowPassFilter.add(mOrientation[0]); + long currentTime = System.currentTimeMillis(); + if (currentTime < mCompassUpdateNextTimestamp) { + return; + } + + mCompassUpdateNextTimestamp = currentTime + UPDATE_RATE_MS; + mGeomagneticField = new GeomagneticField( + (float) mUserLocation.getLatitude(), + (float) mUserLocation.getLongitude(), + (float) mUserLocation.getAltitude(), + currentTime); + mCompassBearing = (float) Math.toDegrees(mLowPassFilter.average()) + mGeomagneticField.getDeclination(); + setCompass(mCompassBearing); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // TODO add accuracy to the equiation + } + + private class AngleLowPassFilter { + + private final int LENGTH = 5; + + private float sumSin, sumCos; + + private ArrayDeque