0

我正在尝试编写代码以通过 GPS 跟踪大约 100 辆汽车的实时位置。我想通过在最后一个 X/Y 点和当前点之间的插值路径上设置其位置,为每辆车平滑地“动画化”谷歌地图标记。我通过 setInterval 调用每 15 秒调用一次 URL 以获取包含所有当前车辆位置的 JSON 对象。在其中,我遍历 JSON 对象中的每个车辆并设置车辆位置。我有动画运动的功能,但它只对一辆车可靠地工作,我相信因为我的嵌套 setInterval 函数不会在它所包含的 for 循环的下一步之前完成。无论如何要让内部 setInterval 函数运行到完成在我的 for 循环中的下一个“i”之前?

setInterval(function() {
   $(document).ready(function() {
      $.getJSON("http://localhost:8080/portal/frfeed/query/tampa_sw/paraVehicle?r=" + Math.random(),function(vehicles){
          $.each(vehicles, function(index, d){ 

             if(d.heading>=0 && d.heading<22.5) direction="NORTH";
             else if(d.heading>=22.5 && d.heading<67.5) direction="NORTHEAST";
             else if(d.heading>=67.5 && d.heading<112.5) direction="EAST";
             else if(d.heading>=112.5 && d.heading<157.5) direction="SOUTHEAST";
             else if(d.heading>=157.5 && d.heading<202.5) direction="SOUTH";
             else if(d.heading>=202.5 && d.heading<247.5) direction="SOUTHWEST";
             else if(d.heading>=247.5 && d.heading<292.5) direction="WEST";
             else if(d.heading>=292.5 && d.heading<338) direction="NORTHWEST";
             else direction="NORTH";
             vehicle = "";

              for (var i=0; i<vMarkers.length; i++) {
                  if( vMarkers[i][0] === d.internalVehicleId ) {
                      var path; 
                      var latlng = new google.maps.LatLng(d.latitude,d.longitude);
                      vMarkers[i][2] = vMarkers[i][1].getPosition().lat();
                      vMarkers[i][3] = vMarkers[i][1].getPosition().lng();
                      vMarkers[i][4] = latlng;
                      vMarkers[i][1].setTitle('Vehicle: ' + d.internalVehicleId + '\r\n' + 'Last Update: ' + d.time + '\r\n' + 'Traveling: ' + direction + ' @ ' + d.speed + ' mph');
                      path = vPolys[i][1].getPath();
                      path.push(latlng);
                      vPolys[i][1].setPath(path);
                      vehicle = vMarkers[i][0];

                      var lat = vMarkers[i][2];
                      var lng = vMarkers[i][3];
                      var latlngTo = vMarkers[i][4];
                      var latLngFrom = new google.maps.LatLng(lat,lng);                            
                      j = 0;
// function below only works correctly if filtered for one vehicle as below, otherwise, all
// markers randomly move and don't stop due to the setInterval being called inside the for loop

                      if (distance(latlngTo.lat(), latlngTo.lng(),latLngFrom.lat(), latLngFrom.lng()) > 20 && vMarkers[i][0] == "1329") {
                           iv = window.setInterval(function() { 
                               j++;
                               var pos = mercatorInterpolate(map, latLngFrom, latlngTo, j/50);
                               vMarkers[i][1].setPosition(pos);
                               if (j >= 50) {
                                    window.clearInterval(iv);
                                        }
                                    }, 20);
                                }
                                else {
                                    vMarkers[i][1].setPosition(latlngTo);
                                };

                                break;
                            }
                        }
                        if( vehicle == "") {
                            color = get_random_color();
                            marker = new StyledMarker({
                                styleIcon:new StyledIcon(StyledIconTypes.BUBBLE,{color:color, fore: "ffffff",text: d.internalVehicleId}),
                                position: new google.maps.LatLng(d.latitude,d.longitude),
                                title:    'Vehicle: ' + d.internalVehicleId + '\r\n' + 'Last Update: ' + d.time + '\r\n' + 'Traveling: ' + direction + ' @ ' + d.speed + ' mph',
                                map: map
                            });
                            var polyOptions = {
                                strokeColor: color,
                                strokeOpacity: 1.0,
                                map: map,
                                strokeWeight: 3
                            };                                
                            poly = new google.maps.Polyline(polyOptions);
                            var latlng = new google.maps.LatLng(d.latitude,d.longitude);
                            vMarkers.push([d.internalVehicleId, marker, d.latitude, d.longitude, latlng]);
                            var path = poly.getPath();
                            path.push(latlng);
                            poly.setPath(path);
                            vPolys.push([d.internalVehicleId, poly])
                            vehicle = "";
                        }
                    });//$.each(vehicles, function(index, d){

                    function mercatorInterpolate(map, latLngFrom, latLngTo, fraction) {
                        // Get projected points
                        var projection = map.getProjection();
                        var pointFrom = projection.fromLatLngToPoint(latLngFrom);
                        var pointTo = projection.fromLatLngToPoint(latLngTo);
                        // Adjust for lines that cross the 180 meridian
                        if (Math.abs(pointTo.x - pointFrom.x) > 128) {
                            if (pointTo.x > pointFrom.x)
                                pointTo.x -= 256;
                            else
                                pointTo.x += 256;
                        }
                        // Calculate point between
                        var x = pointFrom.x + (pointTo.x - pointFrom.x) * fraction;
                        var y = pointFrom.y + (pointTo.y - pointFrom.y) * fraction;
                        var pointBetween = new google.maps.Point(x, y);
                        // Project back to lat/lng
                        var latLngBetween = projection.fromPointToLatLng(pointBetween);
                        return latLngBetween;
                    }

                    function distance(lat1,lon1,lat2,lon2) {
                        var R = 6371;
                        var dLat = (lat2-lat1) * Math.PI / 180;
                        var dLon = (lon2-lon1) * Math.PI / 180; 
                        var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                            Math.cos(lat1 * Math.PI / 180 ) * Math.cos(lat2 * Math.PI / 180 ) * 
                            Math.sin(dLon/2) * Math.sin(dLon/2); 
                        var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
                        var d = R * c;
                        return Math.abs(d*1000);
                    }
                }); //$.getJSON(...., function(vehicles) {
            }); //$(document).ready(function() {
        }, 16000); // setInterval(function(){
4

2 回答 2

1

不,setInterval 是异步的。您必须以使用异步代码的方式进行编程,而不是试图强制它同步。

对于动画,您应该真正使用requestAnimationFrame以获得平滑的结果。

我会创建一个框架数组,每 15 秒推送一个汽车数组。每辆车都存储它的位置

var frames = [[{x: 10, y:2},{x: 5, y:6}], [{x: 12, y:4},{x: 7, y:8}]]

然后我会使用requestAnimationFrame并插入每辆车的当前位置

var currentFrame = 0;
var startTime = 0;

function update(){
   var currentTime = newDate();
   startTime || startTime = currentTime;

   var elaspedTime = Math.floor(currentTime.getTime() - startTime.getTime())/1000;

   // increment the current frame if 15 seconds have elapsed
   elaspedTime%15 === 0 && currentFrame++;

   // Get the current frame
   var frame = frames[currentFrame],
       nextFrame = frames[++currentFrame];

   // Loop over each car in the frame
   for(var i = 0; i < frame.length; i++){
     // Calculate the difference in location
     var xDiff = nextFrame[i].x - frame[i].x;
     var yDiff = nextFrame[i].y - frame[i].y;

     // interpolate the current position of the cars
     var xPos = xDiff / elaspedTime%15;
     var yPos = yDiff / elaspedTime%15;

     // do some work here to set the position of the cars
   }

   requestAnimationFrame(update);
}

requestAnimationFrame(update);

您可以比我所做的更好地优化它,但这就是我的处理方式。

于 2013-10-29T18:12:36.183 回答
0

与其尝试运行多个setInterval(),不如只运行一个,然后在一个函数调用中迭代所有车辆。

例如:

iv = setInterval(function() {
  for (int i=0; i<vehicleArray.length;i++) {
     // Do stuff for each vehicle.
  }
}, 40);

请注意,setInterval()不保证调用它的频率,只保证最小间隔。这可能导致随意跟踪。通过每次进入setInterval()函数时读取时钟并以此计算新位置来避免这种情况。

您的代码试图达到每秒 50 帧,这可能被证明是乐观的。您可以获得一半的平滑效果。即以 40ms 的间隔。

于 2013-10-29T17:47:13.827 回答