谷歌地图JavaScript API v3基于已知路线的标记动画

时间:2013-01-24 20:41:20

标签: javascript google-maps-api-3

我一直在寻找使用Google Maps JavaScript API V3制作标记动画的好方法。标记将在使用纬度和经度坐标定义的预定路径上进行动画处理。

对于我所做的所有研究,我仍然找不到与JavaScript Google Maps API版本3兼容的解决方案。查看之前的this StackOverflow帖子,显然可以使用API​​的版本2进行动画处理,使用GRoute并使用计时器将标记的位置设置为沿着路径的点。

以下是之前建议的代码。我理解它是如何在逻辑上工作的,但我不知道如何将其移植到使用Google Maps API的第3版:

function moveToStep(yourmarker,yourroute,c) {
    if {yourroute.getNumSteps() > c) {
        yourmarker.setLatLng(yourroute.getStep(c).getLatLng());
        window.setTimeout(function(){
            moveToStep(yourmarker,yourroute,c+1);
        },500);
    }
}

moveToStep(marker,route,0);

没有提及GRoutegetNumSteps(我假设返回给定路线上定义的坐标数量setLatLng(我相信哪个得到纬度和经度)标记的坐标),或moveToStep(实际上移动标记)在版本3 full documentation and reference中。

似乎Google已经完全将API从版本2改写为版本3,因为这些函数(看起来非常基本)都已被删除或重命名(我不确定是哪个。)

我在JavaScript API第3版中看到的动画的唯一提及是,当标记首次出现在地图上时,通过使标记为BOUNCEDROP来设置动画。然而,这些实际上并不移动标记的纬度/经度坐标,而仅仅是它们放置在地图上的方式。 API参考here中提到了这两个标记动画。

在上述相同的StackOverflow帖子中,使用JavaScript API为标记动画的工作示例提供了link。但是,正如评论者指出的那样,动画是使用该库的早期版本完成的(可用here。)

最终,我想我有两个问题:

1:是否可以使用Google Maps V3 API为指定路径上的标记设置动画?

如果没有,那么

2:我是否会被迫使用已弃用的库来实现这一目标,还是有其他已知的解决方案?

非常感谢您提供的有助于解决此问题的任何贡献!

2 个答案:

答案 0 :(得分:5)

是。可以使用v3中DirectionsService的路径为标记设置动画。

This example使用移植到v3的Mike Williams的epoly库版本。

proof of concept fiddle

代码段

 var map;
 var directionDisplay;
 var directionsService;
 var stepDisplay;
 var markerArray = [];
 var position;
 var marker = null;
 var polyline = null;
 var poly2 = null;
 var speed = 0.000005,
   wait = 1;
 var infowindow = null;

 var myPano;
 var panoClient;
 var nextPanoId;
 var timerHandle = null;

 function createMarker(latlng, label, html) {
   // alert("createMarker("+latlng+","+label+","+html+","+color+")");
   var contentString = '<b>' + label + '</b><br>' + html;
   var marker = new google.maps.Marker({
     position: latlng,
     map: map,
     title: label,
     zIndex: Math.round(latlng.lat() * -100000) << 5
   });
   marker.myname = label;
   // gmarkers.push(marker);

   google.maps.event.addListener(marker, 'click', function() {
     infowindow.setContent(contentString);
     infowindow.open(map, marker);
   });
   return marker;
 }


 function initialize() {
   infowindow = new google.maps.InfoWindow({
     size: new google.maps.Size(150, 50)
   });
   // Instantiate a directions service.
   directionsService = new google.maps.DirectionsService();

   // Create a map and center it on Manhattan.
   var myOptions = {
     zoom: 13,
     mapTypeId: google.maps.MapTypeId.ROADMAP
   }
   map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

   address = 'new york'
   geocoder = new google.maps.Geocoder();
   geocoder.geocode({
     'address': address
   }, function(results, status) {
     map.setCenter(results[0].geometry.location);
   });

   // Create a renderer for directions and bind it to the map.
   var rendererOptions = {
     map: map
   }
   directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);

   // Instantiate an info window to hold step text.
   stepDisplay = new google.maps.InfoWindow();

   polyline = new google.maps.Polyline({
     path: [],
     strokeColor: '#FF0000',
     strokeWeight: 3
   });
   poly2 = new google.maps.Polyline({
     path: [],
     strokeColor: '#FF0000',
     strokeWeight: 3
   });
 }



 var steps = []

 function calcRoute() {

   if (timerHandle) {
     clearTimeout(timerHandle);
   }
   if (marker) {
     marker.setMap(null);
   }
   polyline.setMap(null);
   poly2.setMap(null);
   directionsDisplay.setMap(null);
   polyline = new google.maps.Polyline({
     path: [],
     strokeColor: '#FF0000',
     strokeWeight: 3
   });
   poly2 = new google.maps.Polyline({
     path: [],
     strokeColor: '#FF0000',
     strokeWeight: 3
   });
   // Create a renderer for directions and bind it to the map.
   var rendererOptions = {
     map: map
   }
   directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);

   var start = document.getElementById("start").value;
   var end = document.getElementById("end").value;
   var travelMode = google.maps.DirectionsTravelMode.DRIVING

   var request = {
     origin: start,
     destination: end,
     travelMode: travelMode
   };

   // Route the directions and pass the response to a
   // function to create markers for each step.
   directionsService.route(request, function(response, status) {
     if (status == google.maps.DirectionsStatus.OK) {
       directionsDisplay.setDirections(response);

       var bounds = new google.maps.LatLngBounds();
       var route = response.routes[0];
       startLocation = new Object();
       endLocation = new Object();

       // For each route, display summary information.
       var path = response.routes[0].overview_path;
       var legs = response.routes[0].legs;
       for (i = 0; i < legs.length; i++) {
         if (i == 0) {
           startLocation.latlng = legs[i].start_location;
           startLocation.address = legs[i].start_address;
           // marker = google.maps.Marker({map:map,position: startLocation.latlng});
           marker = createMarker(legs[i].start_location, "start", legs[i].start_address, "green");
         }
         endLocation.latlng = legs[i].end_location;
         endLocation.address = legs[i].end_address;
         var steps = legs[i].steps;
         for (j = 0; j < steps.length; j++) {
           var nextSegment = steps[j].path;
           for (k = 0; k < nextSegment.length; k++) {
             polyline.getPath().push(nextSegment[k]);
             bounds.extend(nextSegment[k]);



           }
         }
       }

       polyline.setMap(map);
       map.fitBounds(bounds);
       //        createMarker(endLocation.latlng,"end",endLocation.address,"red");
       map.setZoom(18);
       startAnimation();
     }
   });
 }



 var step = 50; // 5; // metres
 var tick = 100; // milliseconds
 var eol;
 var k = 0;
 var stepnum = 0;
 var speed = "";
 var lastVertex = 1;


 //=============== animation functions ======================
 function updatePoly(d) {
   // Spawn a new polyline every 20 vertices, because updating a 100-vertex poly is too slow
   if (poly2.getPath().getLength() > 20) {
     poly2 = new google.maps.Polyline([polyline.getPath().getAt(lastVertex - 1)]);
     // map.addOverlay(poly2)
   }

   if (polyline.GetIndexAtDistance(d) < lastVertex + 2) {
     if (poly2.getPath().getLength() > 1) {
       poly2.getPath().removeAt(poly2.getPath().getLength() - 1)
     }
     poly2.getPath().insertAt(poly2.getPath().getLength(), polyline.GetPointAtDistance(d));
   } else {
     poly2.getPath().insertAt(poly2.getPath().getLength(), endLocation.latlng);
   }
 }


 function animate(d) {
   // alert("animate("+d+")");
   if (d > eol) {
     map.panTo(endLocation.latlng);
     marker.setPosition(endLocation.latlng);
     return;
   }
   var p = polyline.GetPointAtDistance(d);
   map.panTo(p);
   marker.setPosition(p);
   updatePoly(d);
   timerHandle = setTimeout("animate(" + (d + step) + ")", tick);
 }


 function startAnimation() {
   eol = google.maps.geometry.spherical.computeLength(polyline.getPath());
   map.setCenter(polyline.getPath().getAt(0));
   // map.addOverlay(new google.maps.Marker(polyline.getAt(0),G_START_ICON));
   // map.addOverlay(new GMarker(polyline.getVertex(polyline.getVertexCount()-1),G_END_ICON));
   // marker = new google.maps.Marker({location:polyline.getPath().getAt(0)} /* ,{icon:car} */);
   // map.addOverlay(marker);
   poly2 = new google.maps.Polyline({
     path: [polyline.getPath().getAt(0)],
     strokeColor: "#0000FF",
     strokeWeight: 10
   });
   // map.addOverlay(poly2);
   setTimeout("animate(50)", 2000); // Allow time for the initial map display
 }


 //=============== ~animation funcitons =====================


 google.maps.event.addDomListener(window, "load", initialize);
 /*********************************************************************\
 *                                                                     *
 * epolys.js                                          by Mike Williams *
 * updated to API v3                                  by Larry Ross    *
 *                                                                     *
 * A Google Maps API Extension                                         *
 *                                                                     *
 * Adds various Methods to google.maps.Polygon and google.maps.Polyline *
 *                                                                     *
 * .Contains(latlng) returns true is the poly contains the specified   *
 *                   GLatLng                                           *
 *                                                                     *
 * .Area()           returns the approximate area of a poly that is    *
 *                   not self-intersecting                             *
 *                                                                     *
 * .Distance()       returns the length of the poly path               *
 *                                                                     *
 * .Bounds()         returns a GLatLngBounds that bounds the poly      *
 *                                                                     *
 * .GetPointAtDistance() returns a GLatLng at the specified distance   *
 *                   along the path.                                   *
 *                   The distance is specified in metres               *
 *                   Reurns null if the path is shorter than that      *
 *                                                                     *
 * .GetPointsAtDistance() returns an array of GLatLngs at the          *
 *                   specified interval along the path.                *
 *                   The distance is specified in metres               *
 *                                                                     *
 * .GetIndexAtDistance() returns the vertex number at the specified    *
 *                   distance along the path.                          *
 *                   The distance is specified in metres               *
 *                   Returns null if the path is shorter than that      *
 *                                                                     *
 * .Bearing(v1?,v2?) returns the bearing between two vertices          *
 *                   if v1 is null, returns bearing from first to last *
 *                   if v2 is null, returns bearing from v1 to next    *
 *                                                                     *
 *                                                                     *
 ***********************************************************************
 *                                                                     *
 *   This Javascript is provided by Mike Williams                      *
 *   Blackpool Community Church Javascript Team                        *
 *   http://www.blackpoolchurch.org/                                   *
 *   http://econym.org.uk/gmap/                                        *
 *                                                                     *
 *   This work is licenced under a Creative Commons Licence            *
 *   http://creativecommons.org/licenses/by/2.0/uk/                    *
 *                                                                     *
 ***********************************************************************
 *                                                                     *
 * Version 1.1       6-Jun-2007                                        *
 * Version 1.2       1-Jul-2007 - fix: Bounds was omitting vertex zero *
 *                                add: Bearing                         *
 * Version 1.3       28-Nov-2008  add: GetPointsAtDistance()           *
 * Version 1.4       12-Jan-2009  fix: GetPointsAtDistance()           *
 * Version 3.0       11-Aug-2010  update to v3                         *
 *                                                                     *
 \*********************************************************************/


 google.maps.LatLng.prototype.latRadians = function() {
   return this.lat() * Math.PI / 180;
 }

 google.maps.LatLng.prototype.lngRadians = function() {
   return this.lng() * Math.PI / 180;
 }


 // === A method which returns a GLatLng of a point a given distance along the path ===
 // === Returns null if the path is shorter than the specified distance ===
 google.maps.Polyline.prototype.GetPointAtDistance = function(metres) {
   // some awkward special cases
   if (metres == 0) return this.getPath().getAt(0);
   if (metres < 0) return null;
   if (this.getPath().getLength() < 2) return null;
   var dist = 0;
   var olddist = 0;
   for (var i = 1;
     (i < this.getPath().getLength() && dist < metres); i++) {
     olddist = dist;
     dist += google.maps.geometry.spherical.computeDistanceBetween(this.getPath().getAt(i), this.getPath().getAt(i - 1));
   }
   if (dist < metres) {
     return null;
   }
   var p1 = this.getPath().getAt(i - 2);
   var p2 = this.getPath().getAt(i - 1);
   var m = (metres - olddist) / (dist - olddist);
   return new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m);
 }

 // === A method which returns the Vertex number at a given distance along the path ===
 // === Returns null if the path is shorter than the specified distance ===
 google.maps.Polyline.prototype.GetIndexAtDistance = function(metres) {
   // some awkward special cases
   if (metres == 0) return this.getPath().getAt(0);
   if (metres < 0) return null;
   var dist = 0;
   var olddist = 0;
   for (var i = 1;
     (i < this.getPath().getLength() && dist < metres); i++) {
     olddist = dist;
     dist += google.maps.geometry.spherical.computeDistanceBetween(this.getPath().getAt(i), this.getPath().getAt(i - 1));
   }
   if (dist < metres) {
     return null;
   }
   return i;
 }
html {
  height: 100%;
}
body {
  height: 100%;
  margin: 0px;
  font-family: Helvetica, Arial;
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry"></script>
<div id="tools">
  start:
  <input type="text" name="start" id="start" value="union square, NY" />end:
  <input type="text" name="end" id="end" value="times square, NY" />
  <input type="submit" onclick="calcRoute();" />
</div>

<div id="map_canvas" style="width:100%;height:100%;"></div>

答案 1 :(得分:2)

一种方法是沿路线生成一系列位置(例如500个不同的位置),然后使用例如setTimeout每隔X毫秒调用marker.setPosition(nextLocation)

或者,您可以使用symbol on your polyline,在路径中设置百分比,再次使用setTimeout方法。这样做的好处是你不需要计算位置,你可以在每X毫秒内慢慢移动它一小部分。