Skip to content

Instantly share code, notes, and snippets.

@broady
Last active August 19, 2024 12:30
Show Gist options
  • Save broady/6314689 to your computer and use it in GitHub Desktop.
Save broady/6314689 to your computer and use it in GitHub Desktop.
Animating Markers
/* Copyright 2013 Google Inc.
Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0.html */
package com.example.latlnginterpolation;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Property;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
public class MarkerAnimation {
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)
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();
}
}
/* Copyright 2013 Google Inc.
Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0.html */
package com.example.latlnginterpolation;
import com.google.android.gms.maps.model.LatLng;
import static java.lang.Math.asin;
import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.pow;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;
import static java.lang.Math.toDegrees;
import static java.lang.Math.toRadians;
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)));
}
}
}
@slightfoot
Copy link

Sorry, I only just noticed this comment. Thanks for elaborating. So regular views and GL views are rendered in parallel to each other. So this means you could have 2 rendering passes of the regular views whilst GL render pass might skip frames because it takes longer on older hardware?

Could you not add a relative time value to the Marker.setPosition so as-to to provide a smooth transition in the GL renderer?

@runningcode
Copy link

Android studio is telling me that the method ValueAnimator.getAnimatedFraction() is not available on HONEYCOMB, only HONEYCOMB_MR1 and up.

@mtevrst
Copy link

mtevrst commented Oct 9, 2014

In the animation methods, we have passed a LatLngInterpolator and called its interpolate method - but which of the three interpolation methods (Linear, LinearFixed, Spherical) will this invoke?

@aliceresponde
Copy link

Hi, broady thanks for your support.
I have an specific question? ...

I want to delete just one of my maps items (maybe a marker), I dont want yo clean all my map only remove that element using API V2. Its posible?

@syameshk
Copy link

Hi @aliceresponde , you can remove a marker by calling marker.remove(). 😄

@vipGit
Copy link

vipGit commented Apr 10, 2015

@broady Thanks for the code. I am trying to implement animation of markers, But for some reason the markers flicker and appear at the centre of the Map when animated from one Location to another. Do you know the reason why that may happen ?

@vimlesh
Copy link

vimlesh commented Jun 26, 2015

Hi, Broady thank u so much for your support. I have tried your code (animateMarkerToGB,animateMarkerToHC,animateMarkerToICS) for animating marker from one point to other point. Markers are doing smooth transition but after some time markers start stuttering. Can you please suggest a fix or if you had already fixed it then can you please update it.

Also if anyone on this forum has done some fix then please let me know.

Thanks

@adolfdsilva
Copy link

Hi,

I want to animate markers, but on a given lineString of geoPoints. For this I've tried below approach.
I've a list of geopoints. But my problem is the animation ends before the last point is reached.
If i dynamically allocate animation duration, it takes to long for the marker to animate from start to end.
I don't want it to be more than 3sec.

public class Polyline implements LatLngInterpolator {

    ArrayList<GeoPoint> geoPoints;
    int i;

    public Polyline(ArrayList<GeoPoint> geoPoints) {
        this.geoPoints = geoPoints;
    }

    public ArrayList<GeoPoint> getGeoPoints() {
        return geoPoints;
    }

    @Override
    public GeoPoint interpolate(float fraction, GeoPoint a, GeoPoint b) {
        if (i < geoPoints.size())
            return geoPoints.get(i++);
        return null;
    }
}

@banny310
Copy link

Here is my approach for animate marker along a path. It is also supported that origin and destination marker point is outside path. In that case first movement is to reach nearest point in path in line. Same thing happens at the end when last point is outside path.

public class Path implements LatLngInterpolator {

    private List<LatLng> mPath;
    private int mFromIdx;
    private int mToIdx;
    private float mFromFraction;
    private float mToFraction;
    private LinearFixed mOffPathInterpolator = new LinearFixed();
    private boolean mComputed;

    public Path(List<LatLng> path) {
        mPath = path;
        mComputed = false;
    }

    @Override
    public LatLng interpolate(float fraction, LatLng a, LatLng b) {
        if(!mComputed) {
            compute(a, b);
            mComputed = true;
        }

        if(fraction < mFromFraction) {
            // linear interpolation from
            // origin to first point on path
            return mOffPathInterpolator.interpolate(fraction / mFromFraction, a, mPath.get(mFromIdx));
        } else //
        if(fraction > mToFraction) {
            // linear interpolation
            // off path to destination
            return mOffPathInterpolator.interpolate((fraction - mToFraction) / (1f - mToFraction), mPath.get(mToIdx), b);
        } else {
            int idx = Math.round((fraction - mFromFraction) * (mToIdx - mFromIdx));
            return mPath.get(idx);
        }
    }

    private void compute(LatLng a, LatLng b) {
        mFromIdx = LatLngUtils.getNearestPointIdx(mPath, a);
        mToIdx = LatLngUtils.getNearestPointIdx(mPath, b);

        int distancePath = (int) LatLngUtils.calculatePathDistance(mPath, mFromIdx, mToIdx);
        int distanceA = (int) LatLngUtils.distance(a, mPath.get(mFromIdx));
        int distanceB = (int) LatLngUtils.distance(b, mPath.get(mToIdx));
        int distance = distanceA + distancePath + distanceB;

        mFromFraction = (float) (((double) distanceA) / ((double) distance));
        mToFraction = (float) (1f - ((double) distanceB) / ((double) distance));

        Log.d("LatLngInterpolator", "compute: mFromIdx=" + mFromIdx + ", mToIdx=" + mToIdx +
                ", distancePath=" + distancePath + ", distanceA=" + distanceA +
                ", distanceB=" + distanceB + ", distance" + distance +
                ", mFromFraction=" + mFromFraction + ", mToFraction=" + mToFraction);
    }
}

@the-jedi-droid
Copy link

@banny310 Is Path Interpolator useful for two points as well ?

@raxsix
Copy link

raxsix commented Feb 18, 2016

Hei, is it possible to animate Polyline? I have a polyline between my current location and a marker. The marker will get new position every 3 seconds. I get the marker animation working with this method

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    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();
    }

It's cool and smooth, like it
I need that the polyline could follow that marker

@hebers23
Copy link

hebers23 commented Apr 7, 2016

i'm trying this on the Jelly Bean API but it doesn't seem to work i get an error on the final LatLngInterpolator and on the return latLngInterpolator.interpolate part does anyone know how can i repair this?

@khetiyachintan
Copy link

khetiyachintan commented May 7, 2016

Thanks It's Working Grate!!!

/*Add Marker */
Marker marker = mMap.addMarker(new MarkerOptions().position(india).title("India"));
LatLngInterpolator latLngInterpolator = new LatLngInterpolator.Spherical();
MarkerAnimation.animateMarkerToGB(marker, new LatLng(23.03, 72.52), latLngInterpolator);

I was trying to set position of marker and then calling above animation after spending some time on this found the bug and solved it!

Can anyone help me use marker as GIF image, or kind of animation apply on marker.

Thanks.

@pankaj1234
Copy link

Hello benny,
didnot found LatLngUtils class for animate marker along a path.
please provide LatLngUtils class.

@pat2016
Copy link

pat2016 commented Jul 25, 2016

I am interested in the MarkerAnimation routine. I have a version of it working, but I would like to be able to control it such as Pause, Restart and Stop. I have tried obvious techniques (such as introducing code from outside the loop) but these cause the app to crash. I think this is because it is within the run loop and only static variables are in the loop and they cannot be altered.
Does anyone have any suggestions of how such controls might be achieved?

Regards

@pat2016
Copy link

pat2016 commented Jul 26, 2016

Following my comments 17 hours ago, I have successfully enabled Start, Pause, Restart, Stop by placing the control code at the start of the Runnable() section rather than deeper in the run() section. However, I cannot start a new animation until the app is closed and restarted. The app crashes. I've tried handler.removeCallbacksAndMessages(null) but this does not help.
Any suggestions?

@pat2016
Copy link

pat2016 commented Jul 26, 2016

Problem solved. I hadn't reinitialized a variable.

@muazfr24
Copy link

Is there anyone who guide me about LatLngUtils class. I did't found

@jkhin
Copy link

jkhin commented Oct 11, 2016

HI guys!
I'm thankful for this great job!
Well, I'm trying to animate camera each time like Flat Markers in Google Maps APIs.
But, I don't know how to communicate the Handler with the UI Thread to pass marker's location from Handler.

The problem is:
Meanwhile marker is on animation I want to animateCamera each 1sec or 2 sec uploading the target with the lastLngLng of markerLocation. (I'm sorry for my bad English)

@Akash35p
Copy link

please give one example project i didn't understand Interpolator

@Ankitj13
Copy link

Ankitj13 commented Jun 1, 2017

have try to use new LatLngInterpolator.Spherical() & animateMarkerToICS, and got marker far away from the polyline, can any one help me in that. I want to build a map like Uber/Ola App on which i can track my device on traveling path.

@bhuvan0616
Copy link

@banny310 can you provide LatLngUtils class?

@Swavis
Copy link

Swavis commented Jun 25, 2018

@vimlesh
Markers are doing smooth transition but after some time markers start stuttering
did you find the solution how to fix this issue...
Also if anyone on this forum has done some fix then please let me know.
Thanks

@jonahlynk
Copy link

@Ankitj13 Did you find solution? i have the same requirement. Let me know if you found the solution.

@rodsjo
Copy link

rodsjo commented Jan 24, 2019

@vimlesh and @Swavis
The same is happening to me. The animation is fine for a couple of seconds, then it starts stuttering

@abhimuktheeswarar
Copy link

same problem here, any solution?

@saumilsdk
Copy link

Is this copyrighted code? Code java files has mentioned licensed.

/* Copyright 2013 Google Inc. Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0.html */

@broady
Copy link
Author

broady commented Jun 16, 2021

@saumilsdk you can use this code as long as you follow the license terms of the Apache 2.0 license linked there. The code is copyrighted by Google, yes.

To everyone else: please post your question via the Android community channels or Google Maps API channels https://developers.google.com/maps/support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment