如何根据沿线的距离在Google地图折线上添加标记?

mik*_*ikl 14 javascript google-maps distance intervals polyline

我正在尝试创建一个谷歌地图,用户可以在其中绘制他走/跑/骑自行车的路线,看看他跑了多久.GPolyline使用它的getLength()方法的类在这方面非常有用(至少对于Google Maps API V2),但我想根据距离添加标记,例如1公里,5公里,10公里等的标记,但它似乎没有明显的方法可以根据线的多远来找到折线上的点.有什么建议?

Dan*_*llo 35

已经回答了类似的问题,如何解决这个在SQL Server 2008中的服务器端几个月前,我移植使用相同的算法的JavaScript 谷歌地图API第2版.

为了这个例子,让我们使用一个简单的4点折线,总长度约为8,800米.下面的代码段将定义此折线并在地图上呈现它:

var map = new GMap2(document.getElementById('map_canvas'));

var points = [
   new GLatLng(47.656, -122.360),
   new GLatLng(47.656, -122.343),
   new GLatLng(47.690, -122.310),
   new GLatLng(47.690, -122.270)
];

var polyline = new GPolyline(points, '#f00', 6);

map.setCenter(new GLatLng(47.676, -122.343), 12);
map.addOverlay(polyline);
Run Code Online (Sandbox Code Playgroud)

在我们接近实际算法之前,我们需要一个函数,当给定起点,终点和沿着该行行进的距离时返回目标点.幸运的是,Chris Veness在一些方便的JavaScript实现中计算纬度/经度点之间的距离,方位和更多.

特别是我已经从上面的源代码中采用了以下两种方法来处理Google的GLatLng类:

这些用于GLatLng通过一种方法扩展Google的类moveTowards(),当给定另一个点和以米为单位的距离时,GLatLng当距离从原始点移动到作为参数传递的点时,它将沿着该线返回另一个.

GLatLng.prototype.moveTowards = function(point, distance) {   
   var lat1 = this.lat().toRad();
   var lon1 = this.lng().toRad();
   var lat2 = point.lat().toRad();
   var lon2 = point.lng().toRad();         
   var dLon = (point.lng() - this.lng()).toRad();

   // Find the bearing from this point to the next.
   var brng = Math.atan2(Math.sin(dLon) * Math.cos(lat2),
                         Math.cos(lat1) * Math.sin(lat2) -
                         Math.sin(lat1) * Math.cos(lat2) * 
                         Math.cos(dLon));

   var angDist = distance / 6371000;  // Earth's radius.

   // Calculate the destination point, given the source and bearing.
   lat2 = Math.asin(Math.sin(lat1) * Math.cos(angDist) + 
                    Math.cos(lat1) * Math.sin(angDist) * 
                    Math.cos(brng));

   lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(angDist) *
                            Math.cos(lat1), 
                            Math.cos(angDist) - Math.sin(lat1) *
                            Math.sin(lat2));

   if (isNaN(lat2) || isNaN(lon2)) return null;

   return new GLatLng(lat2.toDeg(), lon2.toDeg());
}
Run Code Online (Sandbox Code Playgroud)

有了这个方法,我们现在可以解决这个问题如下:

  1. 遍历路径的每个点.
  2. 找到迭代中的当前点与下一个点之间的距离.
  3. 如果第2点的距离大于我们在路径上行进所需的距离:

    ...然后目标点位于此点和下一点之间.只需将moveTowards()方法应用于当前点,通过下一个点和行程距离.返回结果并中断迭代.

    其他:

    ...目标点位于迭代中下一个点的路径中.我们需要从沿着路径行进的总距离中减去该点与下一个点之间的距离.使用修改的距离继续迭代.

您可能已经注意到我们可以轻松地递归地实现上述内容,而不是迭代地实现上述内容.所以我们这样做:

function moveAlongPath(points, distance, index) {
   index = index || 0;  // Set index to 0 by default.

   if (index < points.length) {
      // There is still at least one point further from this point.

      // Construct a GPolyline to use its getLength() method.
      var polyline = new GPolyline([points[index], points[index + 1]]);

      // Get the distance from this point to the next point in the polyline.
      var distanceToNextPoint = polyline.getLength();

      if (distance <= distanceToNextPoint) {
         // distanceToNextPoint is within this point and the next. 
         // Return the destination point with moveTowards().
         return points[index].moveTowards(points[index + 1], distance);
      }
      else {
         // The destination is further from the next point. Subtract
         // distanceToNextPoint from distance and continue recursively.
         return moveAlongPath(points,
                              distance - distanceToNextPoint,
                              index + 1);
      }
   }
   else {
      // There are no further points. The distance exceeds the length  
      // of the full path. Return null.
      return null;
   }  
}
Run Code Online (Sandbox Code Playgroud)

使用上面的方法,如果我们定义一个GLatLng点数组,并且我们moveAlongPath()使用这个点数组调用我们的函数并且距离为2500米,它将GLatLng在距离第一个点2.5km的路径上返回一个.

var points = [
   new GLatLng(47.656, -122.360),
   new GLatLng(47.656, -122.343),
   new GLatLng(47.690, -122.310),
   new GLatLng(47.690, -122.270)
];

var destinationPointOnPath = moveAlongPath(points, 2500);

// destinationPointOnPath will be a GLatLng on the path 
// at 2.5km from the start.
Run Code Online (Sandbox Code Playgroud)

因此,我们需要做的就是moveAlongPath()在路径上调用我们需要的每个检查点.如果你需要3公里,5公里和10公里的三个标记,你可以简单地做:

map.addOverlay(new GMarker(moveAlongPath(points, 1000)));
map.addOverlay(new GMarker(moveAlongPath(points, 5000)));
map.addOverlay(new GMarker(moveAlongPath(points, 10000)));
Run Code Online (Sandbox Code Playgroud)

但是请注意,如果我们从路径的总长度进一步请求检查点,则moveAlongPath()可能会返回null,因此在传递之前检查返回值会更明智new GMarker().

我们可以将它们放在一起以实现全面实施.在这个例子中,我们沿着前面定义的8.8km路径每1000米丢弃一个标记:

<!DOCTYPE html>
<html> 
<head> 
   <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> 
   <title>Google Maps - Moving point along a path</title> 
   <script src="http://maps.google.com/maps?file=api&v=2&sensor=false"
           type="text/javascript"></script> 
</head> 
<body onunload="GUnload()"> 
   <div id="map_canvas" style="width: 500px; height: 300px;"></div>

   <script type="text/javascript"> 

   Number.prototype.toRad = function() {
      return this * Math.PI / 180;
   }

   Number.prototype.toDeg = function() {
      return this * 180 / Math.PI;
   }

   GLatLng.prototype.moveTowards = function(point, distance) {   
      var lat1 = this.lat().toRad();
      var lon1 = this.lng().toRad();
      var lat2 = point.lat().toRad();
      var lon2 = point.lng().toRad();         
      var dLon = (point.lng() - this.lng()).toRad();

      // Find the bearing from this point to the next.
      var brng = Math.atan2(Math.sin(dLon) * Math.cos(lat2),
                            Math.cos(lat1) * Math.sin(lat2) -
                            Math.sin(lat1) * Math.cos(lat2) * 
                            Math.cos(dLon));

      var angDist = distance / 6371000;  // Earth's radius.

      // Calculate the destination point, given the source and bearing.
      lat2 = Math.asin(Math.sin(lat1) * Math.cos(angDist) + 
                       Math.cos(lat1) * Math.sin(angDist) * 
                       Math.cos(brng));

      lon2 = lon1 + Math.atan2(Math.sin(brng) * Math.sin(angDist) *
                               Math.cos(lat1), 
                               Math.cos(angDist) - Math.sin(lat1) *
                               Math.sin(lat2));

      if (isNaN(lat2) || isNaN(lon2)) return null;

      return new GLatLng(lat2.toDeg(), lon2.toDeg());
   }

   function moveAlongPath(points, distance, index) {        
      index = index || 0;  // Set index to 0 by default.

      if (index < points.length) {
         // There is still at least one point further from this point.

         // Construct a GPolyline to use the getLength() method.
         var polyline = new GPolyline([points[index], points[index + 1]]);

         // Get the distance from this point to the next point in the polyline.
         var distanceToNextPoint = polyline.getLength();

         if (distance <= distanceToNextPoint) {
            // distanceToNextPoint is within this point and the next. 
            // Return the destination point with moveTowards().
            return points[index].moveTowards(points[index + 1], distance);
         }
         else {
            // The destination is further from the next point. Subtract
            // distanceToNextPoint from distance and continue recursively.
            return moveAlongPath(points,
                                 distance - distanceToNextPoint,
                                 index + 1);
         }
      }
      else {
         // There are no further points. The distance exceeds the length  
         // of the full path. Return null.
         return null;
      }  
   }

   var map = new GMap2(document.getElementById('map_canvas'));

   var points = [
      new GLatLng(47.656, -122.360),
      new GLatLng(47.656, -122.343),
      new GLatLng(47.690, -122.310),
      new GLatLng(47.690, -122.270)
   ];

   var polyline = new GPolyline(points, '#f00', 6);

   var nextMarkerAt = 0;     // Counter for the marker checkpoints.
   var nextPoint = null;     // The point where to place the next marker.

   map.setCenter(new GLatLng(47.676, -122.343), 12);

   // Draw the path on the map.
   map.addOverlay(polyline);

   // Draw the checkpoint markers every 1000 meters.
   while (true) {
      // Call moveAlongPath which will return the GLatLng with the next
      // marker on the path.
      nextPoint = moveAlongPath(points, nextMarkerAt);

      if (nextPoint) {
         // Draw the marker on the map.
         map.addOverlay(new GMarker(nextPoint));

         // Add +1000 meters for the next checkpoint.
         nextMarkerAt += 1000;    
      }
      else {
         // moveAlongPath returned null, so there are no more check points.
         break;
      }            
   }
   </script>
</body> 
</html>
Run Code Online (Sandbox Code Playgroud)

以上示例的屏幕截图,每1000米显示一个标记:

谷歌地图 - 沿路径移动点


Mar*_*ler 5

这些是所需功能的原型:

google.maps.Polygon.prototype.Distance = function() {
   var dist = 0;
   for (var i=1; i < this.getPath().getLength(); i++) {
      dist += this.getPath().getAt(i).distanceFrom(this.getPath().getAt(i-1));
   }
   return dist;
}

google.maps.LatLng.prototype.distanceFrom = function(newLatLng) {
    //var R = 6371; // km (change this constant to get miles)
    var R = 6378100; // meters
    var lat1 = this.lat();
    var lon1 = this.lng();
    var lat2 = newLatLng.lat();
    var lon2 = newLatLng.lng();
    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 d;
}
Run Code Online (Sandbox Code Playgroud)

来源