7

使用 Google Maps API v3,我能够google.maps.Circle在我的地图上创建多个对象。但是,我现在需要以某种方式“连接”它们。我有以下带有多个圆圈的地图:

带有多个圆圈的地图

我现在需要让它看起来像这样:

正确的地图
(来源:pcwp.com

我在整个互联网上寻找解决方案,但无济于事。有任何想法吗?

4

2 回答 2

8

x您可能需要考虑通过在路径的每个点之间增加半径的间隔添加额外的圆圈来解决此问题。这将非常容易实施并且适用于旋风分离器的任何方向。显然, Matti 建议的通过连接所有切线来创建多边形的解决方案会更准确,但您可以将其视为一种可能的替代方案。主要的缺点是它可能需要一些努力才能让它看起来很漂亮,而且它显然会比渲染单个多边形使用更多的客户端资源。

让我们从重新创建地图开始:

<!DOCTYPE html>
<html> 
<head> 
   <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> 
   <title>Google Maps Cyclones</title> 
   <script src="http://maps.google.com/maps/api/js?sensor=false" 
           type="text/javascript"></script> 
</head> 
<body> 
   <div id="map" style="width: 600px; height: 400px"></div> 

   <script type="text/javascript"> 
      var i;

      var mapOptions = { 
         mapTypeId: google.maps.MapTypeId.TERRAIN,
         center: new google.maps.LatLng(28.50, -81.50),
         zoom: 5
      };

      var map = new google.maps.Map(document.getElementById("map"), 
                                    mapOptions);

      var pathPoints = [
         new google.maps.LatLng(25.48, -71.26),
         new google.maps.LatLng(25.38, -73.70),
         new google.maps.LatLng(25.28, -77.00),
         new google.maps.LatLng(25.24, -80.11),
         new google.maps.LatLng(25.94, -82.71),
         new google.maps.LatLng(27.70, -87.14)
      ];

      pathPoints[0].radius = 80;
      pathPoints[1].radius = 100;
      pathPoints[2].radius = 200;
      pathPoints[3].radius = 300;
      pathPoints[4].radius = 350;
      pathPoints[5].radius = 550;

      new google.maps.Polyline({
         path: pathPoints,
         strokeColor: '#00FF00',
         strokeOpacity: 1.0,
         strokeWeight: 3,
         map: map
      });

      for (i = 0; i < pathPoints.length; i++) {
         new google.maps.Circle({
            center: pathPoints[i],
            radius: pathPoints[i].radius * 1000,
            fillColor: '#FF0000',
            fillOpacity: 0.2,
            strokeOpacity: 0.5,
            strokeWeight: 1, 
            map: map
         });
      }

   </script> 
</body> 
</html>

谷歌地图旋风 - 图 1 http://img186.imageshack.us/img186/1197/mp1h.png

我假设你已经到了这一点,因此上面的例子应该是不言自明的。基本上我们刚刚定义了 6 个点和 6 个半径,并且我们已经在地图上渲染了圆圈以及绿色路径。

在继续之前,我们需要定义一些方法来计算从一个点到另一个点的距离和方位角。我们还需要一个方法,当给定方位和从源点行进的距离时返回目标点。幸运的是,Chris Veness 在计算纬度/经度点之间的距离、方位等方面为这些方法提供了非常好的 JavaScript 实现。以下方法已适用于 Google 的google.maps.LatLng

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

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

google.maps.LatLng.prototype.destinationPoint = function(brng, dist) {
   dist = dist / 6371;  
   brng = brng.toRad();  
   var lat1 = this.lat().toRad(), lon1 = this.lng().toRad();

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

   if (isNaN(lat2) || isNaN(lon2)) return null;
   return new google.maps.LatLng(lat2.toDeg(), lon2.toDeg());
}

google.maps.LatLng.prototype.bearingTo = function(point) {
   var lat1 = this.lat().toRad(), lat2 = point.lat().toRad();
   var dLon = (point.lng()-this.lng()).toRad();

   var y = Math.sin(dLon) * Math.cos(lat2);
   var x = Math.cos(lat1)*Math.sin(lat2) -
           Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);

   var brng = Math.atan2(y, x);

   return ((brng.toDeg()+360) % 360);
}

google.maps.LatLng.prototype.distanceTo = function(point) {
   var lat1 = this.lat().toRad(), lon1 = this.lng().toRad();
   var lat2 = point.lat().toRad(), lon2 = point.lng().toRad();
   var dLat = lat2 - lat1;
   var dLon = lon2 - lon1;

   var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
           Math.cos(lat1) * Math.cos(lat2) * 
           Math.sin(dLon/2) * Math.sin(dLon/2);

   return 6371 * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)));
}

然后,我们需要添加另一个循环,在for我们之前用来渲染原始圆的循环内渲染中间圆。以下是它的实现方式:

var distanceStep = 50;    // Render an intermediate circle every 50km.

for (i = 0; i < pathPoints.length; i++) {
   new google.maps.Circle({
      center: pathPoints[i],
      radius: pathPoints[i].radius * 1000,
      fillColor: '#FF0000',
      fillOpacity: 0.2,
      strokeOpacity: 0.5,
      strokeWeight: 1, 
      map: map
   });

   if (i < (pathPoints.length - 1)) {
      distanceToNextPoint = pathPoints[i].distanceTo(pathPoints[i + 1]);
      bearingToNextPoint = pathPoints[i].bearingTo(pathPoints[i + 1]);
      radius = pathPoints[i].radius;
      radiusIncrement = (pathPoints[i + 1].radius - radius) / 
                        (distanceToNextPoint / distanceStep);

      for (j = distanceStep; 
           j < distanceToNextPoint; 
           j += distanceStep, radius += radiusIncrement) {

         new google.maps.Circle({
            center: pathPoints[i].destinationPoint(bearingToNextPoint, j),
            radius: radius * 1000,
            fillColor: '#FF0000',
            fillOpacity: 0.2,
            strokeWeight: 0,
            map: map
         });
      }
   }
}

这就是我们会得到的:

谷歌地图旋风 - 图 2

这就是在原始圆圈周围没有黑色笔划的情况下的样子:

谷歌地图旋风 - 图 3 http://img181.imageshack.us/img181/2908/mp3t.png

正如您可能注意到的那样,主要的挑战是使圆圈具有一致的不透明度,即使它们彼此重叠。有几个选项可以实现这一点,但这可能是另一个问题的主题。

无论如何,以下是此示例的完整实现:

<!DOCTYPE html>
<html> 
<head> 
   <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> 
   <title>Google Maps Cyclones</title> 
   <script src="http://maps.google.com/maps/api/js?sensor=false" 
           type="text/javascript"></script> 
</head> 
<body> 
   <div id="map" style="width: 600px; height: 400px"></div> 

   <script type="text/javascript"> 
      Number.prototype.toRad = function() {
         return this * Math.PI / 180;
      }

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

      google.maps.LatLng.prototype.destinationPoint = function(brng, dist) {
         dist = dist / 6371;  
         brng = brng.toRad();  
         var lat1 = this.lat().toRad(), lon1 = this.lng().toRad();

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

         if (isNaN(lat2) || isNaN(lon2)) return null;
         return new google.maps.LatLng(lat2.toDeg(), lon2.toDeg());
      }

      google.maps.LatLng.prototype.bearingTo = function(point) {
         var lat1 = this.lat().toRad(), lat2 = point.lat().toRad();
         var dLon = (point.lng()-this.lng()).toRad();

         var y = Math.sin(dLon) * Math.cos(lat2);
         var x = Math.cos(lat1)*Math.sin(lat2) -
                 Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);

         var brng = Math.atan2(y, x);

         return ((brng.toDeg()+360) % 360);
      }

      google.maps.LatLng.prototype.distanceTo = function(point) {
         var lat1 = this.lat().toRad(), lon1 = this.lng().toRad();
         var lat2 = point.lat().toRad(), lon2 = point.lng().toRad();
         var dLat = lat2 - lat1;
         var dLon = lon2 - lon1;

         var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
                 Math.cos(lat1) * Math.cos(lat2) * 
                 Math.sin(dLon/2) * Math.sin(dLon/2);

         return 6371 * (2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)));
      }

      var i;
      var j;
      var distanceToNextPoint;
      var bearingToNextPoint;      
      var radius;
      var radiusIncrement;
      var distanceStep = 50;    // Render an intermediate circle every 50km.

      var mapOptions = { 
         mapTypeId: google.maps.MapTypeId.TERRAIN,
         center: new google.maps.LatLng(28.50, -81.50),
         zoom: 5
      };

      var map = new google.maps.Map(document.getElementById("map"), mapOptions);

      var pathPoints = [
         new google.maps.LatLng(25.48, -71.26),
         new google.maps.LatLng(25.38, -73.70),
         new google.maps.LatLng(25.28, -77.00),
         new google.maps.LatLng(25.24, -80.11),
         new google.maps.LatLng(25.94, -82.71),
         new google.maps.LatLng(27.70, -87.14)
      ];

      pathPoints[0].radius = 80;
      pathPoints[1].radius = 100;
      pathPoints[2].radius = 200;
      pathPoints[3].radius = 300;
      pathPoints[4].radius = 350;
      pathPoints[5].radius = 550;

      new google.maps.Polyline({
         path: pathPoints,
         strokeColor: '#00FF00',
         strokeOpacity: 1.0,
         strokeWeight: 3,
         map: map
      });

      for (i = 0; i < pathPoints.length; i++) {
         new google.maps.Circle({
            center: pathPoints[i],
            radius: pathPoints[i].radius * 1000,
            fillColor: '#FF0000',
            fillOpacity: 0.2,
            strokeOpacity: 0.5,
            strokeWeight: 0, 
            map: map
         });

         if (i < (pathPoints.length - 1)) {
            distanceToNextPoint = pathPoints[i].distanceTo(pathPoints[i + 1]);
            bearingToNextPoint = pathPoints[i].bearingTo(pathPoints[i + 1]);
            radius = pathPoints[i].radius;
            radiusIncrement = (pathPoints[i + 1].radius - radius) / 
                              (distanceToNextPoint / distanceStep);

            for (j = distanceStep; 
                 j < distanceToNextPoint; 
                 j += distanceStep, radius += radiusIncrement) {

               new google.maps.Circle({
                  center: pathPoints[i].destinationPoint(bearingToNextPoint, j),
                  radius: radius * 1000,
                  fillColor: '#FF0000',
                  fillOpacity: 0.2,
                  strokeWeight: 0,
                  map: map
               });
            }
         }
      }

   </script> 
</body> 
</html>
于 2010-04-11T01:14:19.043 回答
4

如果它总是沿着一条线的一串圆,您可以一次处理一对相邻的圆,找到与它们相切的两条线,并通过它们的交点将它们连接起来以形成一条连续的路径。添加一些内插的贝塞尔控制点以获得平滑度。

如果您的一串圆圈不像您的第一篇文章中的那样整齐(很多重叠,圆圈内的圆圈等),这可能会中断,但这是一个开始。

于 2010-04-10T19:20:34.320 回答