satti8893
satti8893

Reputation: 256

Rotating Marker in Google Maps API v2

I have followed this answer and its working great. https://stackoverflow.com/a/37048987/4209417

But the issues I'm facing now are:

  1. When I stop at any location, its not stable. I'm getting random bearing value even when I'm not moving
  2. when I'm taking turns its rotating anticlockwise which is wrong. Its should take short turns.

This is the code I'm using :

private double bearingBetweenLocations(LatLng latLng1,LatLng latLng2) {

        double PI = 3.14159;
        double lat1 = latLng1.latitude * PI / 180;
        double long1 = latLng1.longitude * PI / 180;
        double lat2 = latLng2.latitude * PI / 180;
        double long2 = latLng2.longitude * PI / 180;

        double dLon = (long2 - long1);

        double y = Math.sin(dLon) * Math.cos(lat2);
        double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
                * Math.cos(lat2) * Math.cos(dLon);

        double brng = Math.atan2(y, x);

        brng = Math.toDegrees(brng);
        brng = (brng + 360) % 360;

        return brng;
    }

private void rotateMarker(final Marker marker, final float toRotation) {
        if(!isMarkerRotating) {
            final Handler handler = new Handler();
            final long start = SystemClock.uptimeMillis();
            final float startRotation = marker.getRotation();
            final long duration = 2000;

            final Interpolator interpolator = new LinearInterpolator();

            handler.post(new Runnable() {
                @Override
                public void run() {
                    isMarkerRotating = true;

                    long elapsed = SystemClock.uptimeMillis() - start;
                    float t = interpolator.getInterpolation((float) elapsed / duration);

                    float rot = t * toRotation + (1 - t) * startRotation;

                    float bearing =  -rot > 180 ? rot / 2 : rot;

                    marker.setRotation(bearing);

                    CameraPosition camPos = CameraPosition
                            .builder(mMap.getCameraPosition())
                            .bearing(bearing)
                            .target(marker.getPosition())
                            .build();
                    mMap.animateCamera(CameraUpdateFactory.newCameraPosition(camPos));

                    if (t < 1.0) {
                        // Post again 16ms later.
                        handler.postDelayed(this, 16);
                    } else {
                        isMarkerRotating = false;
                    }
                }
            });
        }
    }

In onLocationChanged()

float toRotation = (float) bearingBetweenLocations(toLatLng(oldLocation), toLatLng(newLocation));
rotateMarker(my_marker, toRotation);

Upvotes: 6

Views: 2895

Answers (1)

satti8893
satti8893

Reputation: 256

So finally found my own answer which i will post here, so others could find it useful.

whenever my onLocationChanged updates, i check my old & current locations then i update the marker. Like below.

 double oldLat = oldLocation.getLatitude();
 double oldLng = oldLocation.getLongitude();

 double newLat = newLocation.getLatitude();
 double newLng = newLocation.getLongitude();

 if (oldLat != newLat && oldLng != newLng){
      updateMyLocation(toLatLng(oldLocation), toLatLng(mCurrentLocation));
 }

Also i updated my marker rotation code. This rotates the marker & moves to new location with smooth animation. (adjust duration for smooth animation).

float rotation = (float) SphericalUtil.computeHeading(old, new);
rotateMarker(bus_marker, new, rotation);

private void rotateMarker(final Marker marker, final LatLng destination, final float rotation) {

    if (marker != null) {

        final LatLng startPosition = marker.getPosition();
        final float startRotation = marker.getRotation();

        final LatLngInterpolator latLngInterpolator = new LatLngInterpolator.Spherical();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
        valueAnimator.setDuration(3000); // duration 3 second
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

                try {
                    float v = animation.getAnimatedFraction();
                    LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, destination);
                    float bearing = computeRotation(v, startRotation, rotation);

                    marker.setRotation(bearing);
                    marker.setPosition(newPosition);

                }
                catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        valueAnimator.start();
    }
}
private static float computeRotation(float fraction, float start, float end) {
    float normalizeEnd = end - start; // rotate start to 0
    float normalizedEndAbs = (normalizeEnd + 360) % 360;

    float direction = (normalizedEndAbs > 180) ? -1 : 1; // -1 = anticlockwise, 1 = clockwise
    float rotation;
    if (direction > 0) {
        rotation = normalizedEndAbs;
    } else {
        rotation = normalizedEndAbs - 360;
    }

    float result = fraction * rotation + start;
    return (result + 360) % 360;
}

Upvotes: 5

Related Questions