How to move marker along polyline using google map

前端 未结 3 1083
失恋的感觉
失恋的感觉 2020-11-30 02:32

I am trying to move the marker according to the polyline and with animation. Similar to the below image:

Mapbox is already giving this kind of demo. But I w

3条回答
  •  长情又很酷
    2020-11-30 03:04

    You can use for your task your approach based on custom marker animation: animate separately car movement and car turns throughout all direction points. For this You need 2 kinds of animation:

    1) animation for car movement;

    2) animation for car turn;

    which calls each other on its end (car movement animation on end calls car turn animation and vice versa: car turn animation on its end calls car movement animation and so for all points of car path).

    For example on fig.:

    1) animation for car movement from P0 to P1;

    2) animation for car turn on P1;

    3) animation for car movement from P1 to P2

    and so on.

    Car movement animation can be implemented by method like this:

    private void animateCarMove(final Marker marker, final LatLng beginLatLng, final LatLng endLatLng, final long duration) {
            final Handler handler = new Handler();
            final long startTime = SystemClock.uptimeMillis();
    
            final Interpolator interpolator = new LinearInterpolator();
    
            // set car bearing for current part of path
            float angleDeg = (float)(180 * getAngle(beginLatLng, endLatLng) / Math.PI);
            Matrix matrix = new Matrix();
            matrix.postRotate(angleDeg);
            marker.setIcon(BitmapDescriptorFactory.fromBitmap(Bitmap.createBitmap(mMarkerIcon, 0, 0, mMarkerIcon.getWidth(), mMarkerIcon.getHeight(), matrix, true)));
    
            handler.post(new Runnable() {
                @Override
                public void run() {
                    // calculate phase of animation
                    long elapsed = SystemClock.uptimeMillis() - startTime;
                    float t = interpolator.getInterpolation((float) elapsed / duration);
                    // calculate new position for marker
                    double lat = (endLatLng.latitude - beginLatLng.latitude) * t + beginLatLng.latitude;
                    double lngDelta = endLatLng.longitude - beginLatLng.longitude;
    
                    if (Math.abs(lngDelta) > 180) {
                        lngDelta -= Math.signum(lngDelta) * 360;
                    }
                    double lng = lngDelta * t + beginLatLng.longitude;
    
                    marker.setPosition(new LatLng(lat, lng));
    
                    // if not end of line segment of path 
                    if (t < 1.0) {
                        // call next marker position
                        handler.postDelayed(this, 16);
                    } else {
                        // call turn animation
                        nextTurnAnimation();
                    }
                }
            });
        }
    

    where

    mMarkerIcon is:

    Bitmap mMarkerIcon;
    ...
    mMarkerIcon = BitmapFactory.decodeResource(getResources(), R.drawable.the_car);  // for your car icon in file the_car.png in drawable folder
    

    and car icon should be North oriented:

    for correct rotation apply

    nextTurnAnimation() - method called on end of car movement animation to start car turn animation:

    private void nextTurnAnimation() {
            mIndexCurrentPoint++;
    
            if (mIndexCurrentPoint < mPathPolygonPoints.size() - 1) {
                LatLng prevLatLng = mPathPolygonPoints.get(mIndexCurrentPoint - 1);
                LatLng currLatLng = mPathPolygonPoints.get(mIndexCurrentPoint);
                LatLng nextLatLng = mPathPolygonPoints.get(mIndexCurrentPoint + 1);
    
                float beginAngle = (float)(180 * getAngle(prevLatLng, currLatLng) / Math.PI);
                float endAngle = (float)(180 * getAngle(currLatLng, nextLatLng) / Math.PI);
    
                animateCarTurn(mCarMarker, beginAngle, endAngle, TURN_ANIMATION_DURATION);
            }
        }
    

    In its turn car turn animation method can be like this:

    private void animateCarTurn(final Marker marker, final float startAngle, final float endAngle, final long duration) {
            final Handler handler = new Handler();
            final long startTime = SystemClock.uptimeMillis();
            final Interpolator interpolator = new LinearInterpolator();
    
            final float dAndgle = endAngle - startAngle;
    
            Matrix matrix = new Matrix();
            matrix.postRotate(startAngle);
            Bitmap rotatedBitmap = Bitmap.createBitmap(mMarkerIcon, 0, 0, mMarkerIcon.getWidth(), mMarkerIcon.getHeight(), matrix, true);
            marker.setIcon(BitmapDescriptorFactory.fromBitmap(rotatedBitmap));
    
            handler.post(new Runnable() {
                @Override
                public void run() {
    
                    long elapsed = SystemClock.uptimeMillis() - startTime;
                    float t = interpolator.getInterpolation((float) elapsed / duration);
    
                    Matrix m = new Matrix();
                    m.postRotate(startAngle + dAndgle * t);
                    marker.setIcon(BitmapDescriptorFactory.fromBitmap(Bitmap.createBitmap(mMarkerIcon, 0, 0, mMarkerIcon.getWidth(), mMarkerIcon.getHeight(), m, true)));
    
                    if (t < 1.0) {
                        handler.postDelayed(this, 16);
                    } else {
                        nextMoveAnimation();
                    }
                }
            });
        }
    

    where nextMoveAnimation() is:

    private void nextMoveAnimation() {
            if (mIndexCurrentPoint <  mPathPolygonPoints.size() - 1) {
                animateCarMove(mCarMarker, mPathPolygonPoints.get(mIndexCurrentPoint), mPathPolygonPoints.get(mIndexCurrentPoint+1), MOVE_ANIMATION_DURATION);
            }
        }
    

    The mPathPolygonPoints (geopoints of car trip) is:

    private List mPathPolygonPoints;
    

    And the mIndexCurrentPoint variable is index of current point on path (it should be 0 at start of animation and incremented on each turn of path in nextTurnAnimation() method).

    TURN_ANIMATION_DURATION - duration (in ms) animation for car turn on path geopoint;

    MOVE_ANIMATION_DURATION - duration (in ms) animation for car movement along line segment of path;

    To get bearing You can use method like that:

    private double getAngle(LatLng beginLatLng, LatLng endLatLng) {
            double f1 = Math.PI * beginLatLng.latitude / 180;
            double f2 = Math.PI * endLatLng.latitude / 180;
            double dl = Math.PI * (endLatLng.longitude - beginLatLng.longitude) / 180;
            return Math.atan2(Math.sin(dl) * Math.cos(f2) , Math.cos(f1) * Math.sin(f2) - Math.sin(f1) * Math.cos(f2) * Math.cos(dl));;
        }
    

    Finally You can start all animations by call animateCarMove() once:

    animateCarMove(mCarMarker, mPathPolygonPoints.get(0), mPathPolygonPoints.get(1), MOVE_ANIMATION_DURATION);
    

    other steps of animation will be called automatically for each point of car path.

    And You should take into account some "special cases" like:

    1) changing sign of of turn angle (e.g. bearing changes from -120 to 150 degrees);

    2) possibilities for interrupt of animation by user;

    3) calculate animation duration on path segment length (e.g. 1 sec for 1 km of segment length instead of fixed MOVE_ANIMATION_DURATION)

    4) probably tune value 16 in handler.postDelayed(this, 16); line for better performance;

    5) and so on.

提交回复
热议问题