Smoothly Animating a camera path on a mapview
Asked Answered
E

2

11

I use my MapView (new API v2) with a list of points that belong to a route. They are pretty close together. My problem is, if I do the animation steps calling

        mMapInstance.animateCamera(CameraUpdateFactory.newLatLng(mNextPosition),ms, null);

The cameraman behaves like an intrepid grasshopper, and pans out and in exaggerately, and in the process al the slide cache gets @##@#@#!

What is the best approach to animate a path and get a uniform scrolling experience? Speed is not a problem, I would use low speed, I am interested in the smoothness...

I'll be more than happy if I could simulate a path I do with the finger.. because the map behaves beautifully and has a lot of tiles around cached. But any attempt to programmatically move the map results in the bold animation, white screen, reloading of tiles ...

Thanks in advance !!!

Ebonieebonite answered 12/6, 2013 at 16:3 Comment(0)
E
1

Well, I hope somebody provide a better answer, but with the many experiments I've done, I was unable to get a decent smooth scroll using animateCamera.

Regardless of only changing Lat/Lng for points close together, the cameraman kept making impressive takeoffs and landings.

I have had limited 'el-cheapo' animation success with the following routine:

    private void animateTo(double lat, double lon, double zoom, double bearing, double tilt, final int milliseconds) {

        if (mMapInstance==null) return;
        mMapInstance.setMapType(paramMapMode);
        mCurrentPosition=new LatLng(lat,lon);

        // animate camera jumps too much
        // so we set the camera instantly to the next point

        mMapInstance.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition(mCurrentPosition,(float)zoom, (float)tilt, (float)bearing)));

        // give Android a break so it can load tiles. If I start the animation
        // without pause, no tile loading is done

        mMap.postDelayed(new Runnable(){
            @Override
            public void run() {
                // keeping numbers small you get a nice scrolling effect
                mMapInstance.animateCamera(CameraUpdateFactory.scrollBy(250-(float)Math.random()*500-250, 250-(float)Math.random()*500),milliseconds,null);

            }},500);

    }

This routine, called with milliseconds value in the 10000s, goes to one point then makes a flyover animation in a random direction keeping the damn zoom tranquilo. As the pixel values are very small, chances are it's everything cached.

Anybody with a better solution? Is it reasonable or possible to try to inject touch events to simulate a 'touched' fling?

Ebonieebonite answered 17/6, 2013 at 4:28 Comment(1)
You cannot call postDelayed() on mMap!Miscarry
S
4

I've found that using the optional callback as a final parameter to manifest a recursive solution provides smooth animation. This code zooms in, changes angle for a panoramic spin, then zooms in again; it doesn't like it, though, when the initial parameters and the initial callback are identical; I'm sure there's a better way to call the recursion, but hopefully this can give you an idea of how you can animate functionally:

    //initial zoom
static final int initZoom = 8;
//steps the zoom
int stepZoom = 0;
// number of steps in zoom, be careful with this number!
int stepZoomMax = 5;
//number of .zoom steps in a step
int stepZoomDetent = (18 - initZoom) / stepZoomMax;
//when topause zoom for spin
int stepToSpin = 4;
//steps the spin
int stepSpin = 0;
//number of steps in spin (factor of 360)
int stepSpinMax = 4;
//number of degrees in stepSpin
int stepSpinDetent = 360 / stepSpinMax;

Intent detailIntent;
Intent intent;  
Marker marker;
final int mapHopDelay = 2000;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.map_affirm);
    try
    {MapsInitializer.initialize(this);}
    catch (GooglePlayServicesNotAvailableException impossible)
    {   /* Impossible */ Log.e(TAG, "the impossible occurred");}
    intent = this.getIntent();
    latLng = new LatLng(intent.getDoubleExtra("Latitude", 0.0), intent.getDoubleExtra("Longitude", 0.0));
    map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
    map.animateCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.builder()
                                                            .target(latLng)
                                                            .zoom(initZoom-1)
                                                            .build())
                      , mapHopDelay
                      , cameraAnimation
                      );
    marker = map.addMarker(new MarkerOptions()
                           .draggable(true)
                           .position(latLng)
                           .title("Location of Photographer"));

}

public CancelableCallback cameraAnimation = new CancelableCallback(){

    @Override
    public void onFinish()
    {
        if (stepZoom < stepZoomMax && stepZoom != stepToSpin)
        {
            stepZoom++;
            map.animateCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.builder()
                                                                    .target(latLng)
                                                                    .zoom(initZoom + (stepZoomDetent * (stepZoom - 1)))
                                                                    //   .bearing(40*aniStep)
                                                                    //   .tilt(60)
                                                                    .build()), mapHopDelay, cameraAnimation);

        }
        else if (stepZoom >= stepZoomMax)// ending position hard coded for this application
        {map.animateCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.builder()
                                                                 .target(latLng)
                                                                 .zoom(18)
                                                                 //  .bearing(0)
                                                                 .tilt(0)
                                                                 .build()));
        }
        else
        {
            if (stepSpin <= stepSpinMax)
            {
                stepSpin++;
                map.animateCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.builder()
                                                                        .target(latLng)
                                                                        .zoom(initZoom + stepZoomDetent * stepZoom)
                                                                        .bearing(stepSpinDetent * (stepSpin - 1))
                                                                        .tilt(60)
                                                                        .build()), mapHopDelay, cameraAnimation);
            }
            else
            {
                stepZoom++;
                map.animateCamera(CameraUpdateFactory.newCameraPosition(CameraPosition.builder()
                                                                        .target(latLng)
                                                                        .zoom(initZoom + stepZoomDetent * stepZoom)
                                                                        .bearing(0)
                                                                        .tilt(0)
                                                                        .build()), mapHopDelay, cameraAnimation);
            }
        }
    }

    @Override
    public void onCancel()
    {}

};
Sid answered 16/8, 2013 at 0:12 Comment(1)
hi! Sounds interesting, I haven't used the callback, gonna give it a try. Thanks for posting the code!Ebonieebonite
E
1

Well, I hope somebody provide a better answer, but with the many experiments I've done, I was unable to get a decent smooth scroll using animateCamera.

Regardless of only changing Lat/Lng for points close together, the cameraman kept making impressive takeoffs and landings.

I have had limited 'el-cheapo' animation success with the following routine:

    private void animateTo(double lat, double lon, double zoom, double bearing, double tilt, final int milliseconds) {

        if (mMapInstance==null) return;
        mMapInstance.setMapType(paramMapMode);
        mCurrentPosition=new LatLng(lat,lon);

        // animate camera jumps too much
        // so we set the camera instantly to the next point

        mMapInstance.moveCamera(CameraUpdateFactory.newCameraPosition(new CameraPosition(mCurrentPosition,(float)zoom, (float)tilt, (float)bearing)));

        // give Android a break so it can load tiles. If I start the animation
        // without pause, no tile loading is done

        mMap.postDelayed(new Runnable(){
            @Override
            public void run() {
                // keeping numbers small you get a nice scrolling effect
                mMapInstance.animateCamera(CameraUpdateFactory.scrollBy(250-(float)Math.random()*500-250, 250-(float)Math.random()*500),milliseconds,null);

            }},500);

    }

This routine, called with milliseconds value in the 10000s, goes to one point then makes a flyover animation in a random direction keeping the damn zoom tranquilo. As the pixel values are very small, chances are it's everything cached.

Anybody with a better solution? Is it reasonable or possible to try to inject touch events to simulate a 'touched' fling?

Ebonieebonite answered 17/6, 2013 at 4:28 Comment(1)
You cannot call postDelayed() on mMap!Miscarry

© 2022 - 2024 — McMap. All rights reserved.