如何使用Google地图沿折线移动标记

27

我正在尝试根据折线进行标记移动并具有动画效果,类似于下面的图片:

来自Mapbox

Mapbox 已经提供了这种类型的演示。但是我想通过使用 Google 地图来实现相同的效果。然而,目前我的标记没有沿着路径旋转。 这是我尝试过的内容:

private void onReady(List<LatLng> polyz) {

      for (int i = 0; i < polyz.size() - 1; i++) {
        LatLng src = polyz.get(i);
        LatLng dest = polyz.get(i + 1);
        Polyline line = map.addPolyline(new PolylineOptions()
            .add(new LatLng(src.latitude, src.longitude),
                new LatLng(dest.latitude, dest.longitude))
            .width(2).color(Color.RED).geodesic(true));

      }
      LatLngBounds.Builder builder = new LatLngBounds.Builder();
      builder.include(polyz.get(0));
      builder.include(polyz.get(polyz.size()-1));
      map.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 48));
      map.animateCamera(CameraUpdateFactory.zoomTo(7), 1000, null);
      BitmapDescriptor icon = BitmapDescriptorFactory.fromResource(R.drawable.car);
      marker = map.addMarker(new MarkerOptions()
          .position(polyz.get(0))
          .title("Curr")
          .snippet("Move"));
      marker.setIcon(icon);

    }

还有动画:

    private void animateMarker(GoogleMap myMap, final Marker marker, final List<LatLng> directionPoint,
      final boolean hideMarker) {
    final Handler handler = new Handler();
    final long start = SystemClock.uptimeMillis();
    Projection proj = myMap.getProjection();
    final long duration = 600000;

    final Interpolator interpolator = new LinearInterpolator();

    handler.post(new Runnable() {
      int i = 0;

      @Override
      public void run() {
        long elapsed = SystemClock.uptimeMillis() - start;
        float t = interpolator.getInterpolation((float) elapsed
            / duration);
        Location location=new Location(String.valueOf(directionPoint.get(i)));
        Location newlocation=new Location(String.valueOf(directionPoint.get(i+1)));
        marker.setAnchor(0.5f, 0.5f);
        marker.setRotation(location.bearingTo(newlocation)  - 45);
        if (i < directionPoint.size()) {
          marker.setPosition(directionPoint.get(i));
        }
        i++;

        if (t < 1.0) {
          // Post again 16ms later.
          handler.postDelayed(this, 16);
        } else {
          if (hideMarker) {
            marker.setVisible(false);
          } else {
            marker.setVisible(true);
          }
        }
      }
    });
  }

你解决了吗? - M.Yogeshwaran
3个回答

31
您可以使用基于自定义标记动画的方法来完成任务:分别动画地处理汽车移动和转弯,贯穿整个方向点。为此,您需要两种动画:

1) 汽车行驶动画;

2) 汽车转弯动画;

它们在结束时相互调用(汽车运动动画在结束时调用汽车转弯动画,反之亦然:汽车转弯动画在其结束时调用汽车运动动画,并如此进行所有汽车路径的点)。

例如,在 fig. 上:

enter image description here

1) 从 P0P1 的汽车行驶动画;

2) 在 P1 处的汽车转弯动画;

3) 从 P1P2 的汽车行驶动画

诸如此类。

汽车移动动画可以通过以下方法实现:

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

这里的mMarkerIcon是:

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

汽车图标应该朝北:

enter image description here

为了正确旋转,请应用以下方法:

nextTurnAnimation() - 在汽车移动动画结束时调用的方法,以启动汽车转向动画:

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

在这种情况下,汽车转向的动画方法可以如下所示:
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();
                }
            }
        });
    }

其中nextMoveAnimation()是:

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

mPathPolygonPoints 是汽车行车路线的地理位置点:

private List<LatLng> mPathPolygonPoints;

mIndexCurrentPoint变量是路径上当前点的索引(在动画开始时应为0,并在nextTurnAnimation()方法中每次转弯时递增)。

TURN_ANIMATION_DURATION - 车辆在路径地理点上转弯的动画持续时间(以毫秒为单位);

MOVE_ANIMATION_DURATION - 车辆沿路径线段移动的动画持续时间(以毫秒为单位);

要获取方位角,可以使用类似于以下方法:

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

最后,您只需要调用animateCarMove()一次即可开始所有动画:
animateCarMove(mCarMarker, mPathPolygonPoints.get(0), mPathPolygonPoints.get(1), MOVE_ANIMATION_DURATION);

对于汽车路线的每个点,动画的其他步骤将会自动调用。

您应该考虑一些“特殊情况”,比如:

1)转向角度的符号改变(例如,方位从-120度变为150度);

2)用户中断动画的可能性;

3)根据路径段长度计算动画持续时间(例如,1km的路径长度需要1秒,而不是固定的MOVE_ANIMATION_DURATION);

4)可能需要调整handler.postDelayed(this, 16); 行中的值 16 以获得更好的性能;

5)等等。


1
谢谢您的解释。您能告诉我如何处理这个特殊情况,即转向角度的符号变化(例如,方位角从-120度变为150度)? - user7140906
正如您所说,当我获取角度为-122至171度时,标记旋转的角度超过了需要的角度。如何处理这种情况。 - user7140906
非常感谢!这帮了我很多 :) - Rohit TP
感谢@AndriyOmelchenko.. :) - Kriti
@AndriiOmelchenko谢谢,这个很好用。你能否编辑答案以涵盖以下内容?1)更改转向角度的符号(例如,方位从-120度变为150度); - Mohamed Nageh
显示剩余12条评论

3
问题在于您创建Location对象的方式。您正在使用Location (String provider)构造函数使用命名提供程序构造新位置(文档):

默认情况下,时间、纬度和经度为0,位置没有方位、高度、速度、精度或额外信息。

在您的情况下,您没有创建具有所需坐标的Location,而是创建了一个Location,其提供程序名称为String.valueOf(directionPoint.get(i)),但Location对象的纬度和经度为0。

创建Location对象的正确方法如下:

Location location = new Location(LocationManager.GPS_PROVIDER);
location.setLatitude(directionPoint.get(i).latitude);
location.setLongitude(directionPoint.get(i).longitude);

Location newlocation = new Location(LocationManager.GPS_PROVIDER);
newlocation.setLatitude(directionPoint.get(i+1).latitude);
newlocation.setLongitude(directionPoint.get(i+1).longitude);

请注意,由于您没有考虑到i+1最终将等于directionPoint.size(),因此您将收到ArrayIndexOutOfBoundsException


谢谢您的回答。我已经尝试使用 SphericalUtil.computeHeading(beginLatLng,endLatLng) 进行方位角计算。标记旋转了,但不够平滑,这让人感到痛苦。 - user7140906

1
我想你正在寻找的是标记动画
您可以使标记动起来,以便在各种不同情况下展现动态移动。要指定标记的动画方式,请使用标记的animation属性,类型为google.maps.Animation。支持以下Animation值:
-DROP表示标记应从地图顶部掉落到其最终位置,当首次放置在地图上时。一旦标记停止并静止不动,动画将停止并恢复为null。这种类型的动画通常在创建标记时指定。
-BOUNCE表示标记应在原地弹跳。弹跳标记将继续弹跳,直到其animation属性明确设置为null。
以下是指南中的代码片段:
var marker;

      function initMap() {
        var map = new google.maps.Map(document.getElementById('map'), {
          zoom: 13,
          center: {lat: 59.325, lng: 18.070}
        });

        marker = new google.maps.Marker({
          map: map,
          draggable: true,
          animation: google.maps.Animation.DROP,
          position: {lat: 59.327, lng: 18.067}
        });
        marker.addListener('click', toggleBounce);
      }

      function toggleBounce() {
        if (marker.getAnimation() !== null) {
          marker.setAnimation(null);
        } else {
          marker.setAnimation(google.maps.Animation.BOUNCE);
        }
      }

谢谢您的回答。但是我正在开发非原生Android应用程序,您能否提供Java示例? - user7140906

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接