Rotate Marker and Move Animation on Map Like Uber Android

Rotate Marker and Move Animation on Map like Uber Android

I recently came across the same use-case. Here is my solution on it.

First, I would like to thank @VipiN for sharing "The Smooth Moving Car Code". It works smoothly.

The second part is to place car-marker in the right direction and rotate it according to turns. To achieve this I calculated the bearing or heading angle between two successive points(i.e. location updates you receive from device/server). This link will help you understand the maths behind it.

The following code will give you bearing between two locations:

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;
}

Finally, we need to rotate the car-marker by the angle that we get from above method.

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 = 1000;

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;

marker.setRotation(-rot > 180 ? rot / 2 : rot);
if (t < 1.0) {
// Post again 16ms later.
handler.postDelayed(this, 16);
} else {
isMarkerRotating = false;
}
}
});
}
}

Cheers!

Animate marker to new location like uber car animation whenever driver location (lat lng) changes on the server

Download MarkerAnimation and LatLngInterpolator files from the below links

https://drive.google.com/file/d/1i-pDZYOtf-Vl8m6wnYulGnv3rb9aVMih/view
https://drive.google.com/file/d/1AypW3b6YIX6UtYFFuh1-lx7Id1lt0QqP/view

or Use the below posted code

MarkerAnimation code:

public class MarkerAnimation {
public static void animateMarkerToGB(final Marker marker, final LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
final LatLng startPosition = marker.getPosition();
final Handler handler = new Handler();
final long start = SystemClock.uptimeMillis();
final Interpolator interpolator = new AccelerateDecelerateInterpolator();
final float durationInMs = 3000;

handler.post(new Runnable() {
long elapsed;
float t;
float v;

@Override
public void run() {
// Calculate progress using interpolator
elapsed = SystemClock.uptimeMillis() - start;
t = elapsed / durationInMs;
v = interpolator.getInterpolation(t);

marker.setPosition(latLngInterpolator.interpolate(v, startPosition, finalPosition));

// Repeat till progress is complete.
if (t < 1) {
// Post again 16ms later.
handler.postDelayed(this, 16);
}
}
});
}

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
static void animateMarkerToHC(final Marker marker, final LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
final LatLng startPosition = marker.getPosition();

ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float v = animation.getAnimatedFraction();
LatLng newPosition = latLngInterpolator.interpolate(v, startPosition, finalPosition);
marker.setPosition(newPosition);
}
});
valueAnimator.setFloatValues(0, 1); // Ignored.
valueAnimator.setDuration(3000);
valueAnimator.start();
}

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static void animateMarkerToICS(Marker marker, LatLng finalPosition, final LatLngInterpolator latLngInterpolator) {
TypeEvaluator<LatLng> typeEvaluator = new TypeEvaluator<LatLng>() {
@Override
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
return latLngInterpolator.interpolate(fraction, startValue, endValue);
}
};
Property<Marker, LatLng> property = Property.of(Marker.class, LatLng.class, "position");
ObjectAnimator animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, finalPosition);
animator.setDuration(3000);
animator.start();
}

}

LatLngInterpolator Code :

public interface LatLngInterpolator {
public LatLng interpolate(float fraction, LatLng a, LatLng b);

public class Linear implements LatLngInterpolator {
@Override
public LatLng interpolate(float fraction, LatLng a, LatLng b) {
double lat = (b.latitude - a.latitude) * fraction + a.latitude;
double lng = (b.longitude - a.longitude) * fraction + a.longitude;
return new LatLng(lat, lng);
}
}

public class LinearFixed implements LatLngInterpolator {
@Override
public LatLng interpolate(float fraction, LatLng a, LatLng b) {
double lat = (b.latitude - a.latitude) * fraction + a.latitude;
double lngDelta = b.longitude - a.longitude;

// Take the shortest path across the 180th meridian.
if (Math.abs(lngDelta) > 180) {
lngDelta -= Math.signum(lngDelta) * 360;
}
double lng = lngDelta * fraction + a.longitude;
return new LatLng(lat, lng);
}
}

public class Spherical implements LatLngInterpolator {

/* From github.com/googlemaps/android-maps-utils */
@Override
public LatLng interpolate(float fraction, LatLng from, LatLng to) {
// http://en.wikipedia.org/wiki/Slerp
double fromLat = toRadians(from.latitude);
double fromLng = toRadians(from.longitude);
double toLat = toRadians(to.latitude);
double toLng = toRadians(to.longitude);
double cosFromLat = cos(fromLat);
double cosToLat = cos(toLat);

// Computes Spherical interpolation coefficients.
double angle = computeAngleBetween(fromLat, fromLng, toLat, toLng);
double sinAngle = sin(angle);
if (sinAngle < 1E-6) {
return from;
}
double a = sin((1 - fraction) * angle) / sinAngle;
double b = sin(fraction * angle) / sinAngle;

// Converts from polar to vector and interpolate.
double x = a * cosFromLat * cos(fromLng) + b * cosToLat * cos(toLng);
double y = a * cosFromLat * sin(fromLng) + b * cosToLat * sin(toLng);
double z = a * sin(fromLat) + b * sin(toLat);

// Converts interpolated vector back to polar.
double lat = atan2(z, sqrt(x * x + y * y));
double lng = atan2(y, x);
return new LatLng(toDegrees(lat), toDegrees(lng));
}

private double computeAngleBetween(double fromLat, double fromLng, double toLat, double toLng) {
// Haversine's formula
double dLat = fromLat - toLat;
double dLng = fromLng - toLng;
return 2 * asin(sqrt(pow(sin(dLat / 2), 2) +
cos(fromLat) * cos(toLat) * pow(sin(dLng / 2), 2)));
}
}

}

This is the bit of code that rotates the icon(marker) on the map

private float getBearing(LatLng begin, LatLng end) {
double lat = Math.abs(begin.latitude - end.latitude);
double lng = Math.abs(begin.longitude - end.longitude);

if (begin.latitude < end.latitude && begin.longitude < end.longitude)
return (float) (Math.toDegrees(Math.atan(lng / lat)));
else if (begin.latitude >= end.latitude && begin.longitude < end.longitude)
return (float) ((90 - Math.toDegrees(Math.atan(lng / lat))) + 90);
else if (begin.latitude >= end.latitude && begin.longitude >= end.longitude)
return (float) (Math.toDegrees(Math.atan(lng / lat)) + 180);
else if (begin.latitude < end.latitude && begin.longitude >= end.longitude)
return (float) ((90 - Math.toDegrees(Math.atan(lng / lat))) + 270);
return -1;

}

This is the bit of code that you need to add after getting Lat and Lng from api.

if(marker == null) { 
marker = mMap.addMarker(new MarkerOptions().position(endPosition)
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ROSE)));
MarkerAnimation.animateMarkerToGB(marker, endPosition, new LatLngInterpolator.Spherical());
marker.setRotation(getBearing(endPosition, startPosition));

} else {
MarkerAnimation.animateMarkerToICS(marker, endPosition, new LatLngInterpolator.Spherical());
marker.setRotation(getBearing(endPosition, startPosition));

}

Happy coding :)

Rotate marker/car icon in google maps - Android

there is simple method available for marker

marker.rotation(float value)

Sets the rotation of the marker in degrees clockwise about the marker's anchor point. The axis of rotation is perpendicular to the marker. A rotation of 0 corresponds to the default position of the marker. When the marker is flat on the map, the default position is North aligned and the rotation is such that the marker always remains flat on the map. When the marker is a billboard, the default position is pointing up and the rotation is such that the marker is always facing the camera. The default value is 0.



Related Topics



Leave a reply



Submit