【问题标题】:Multiple circles -> One Polygon?多个圆圈-> 一个多边形?
【发布时间】:2010-04-10 19:12:00
【问题描述】:

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

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


(来源:pcwp.com

我在 Internet 上到处寻找解决方案,但无济于事。有什么想法吗?

【问题讨论】:

  • 顺便说一句,如果你有可能链接到你正在做的事情,我很想看看结果如何。

标签: javascript google-maps google-maps-api-3 bezier geometry


【解决方案1】:

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

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

<!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>

Google Maps Cyclones - Figure 1 http://img186.imageshack.us/img186/1197/mp1h.png

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

在我们继续之前,我们需要定义一些方法来计算从一个点到另一个点的距离和方位角。我们还需要一个方法,当给定方位和从源点行进的距离时,该方法将返回目标点。幸运的是,Chris Veness 在Calculate distance, bearing and more between Latitude/Longitude points 为这些方法提供了一个非常好的 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
         });
      }
   }
}

这就是我们会得到的:

如果没有原始圆圈周围的黑色笔划,这就是它的外观:

Google Maps Cyclones - Figure 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>

【讨论】:

  • 哇,不错的答案。我也必须尝试实现我的,所以我可以发布一些代码(这将是我生疏的几何技能的一个很好的练习)
  • @Matti:我期待看到您的尝试 :) ... 老实说,我打算先尝试实现您的 tangets 方法,但数学变得有点太棘手了凌晨 2 点!...如果您真的决定冒险,请随意重用我的答案代码中的任何部分。
  • 请记住,六个单独的圆圈中的每一个都存在预测错误。不幸的是,我只知道这些点的预测误差,所以它必须是沿另外 50 公里圈的平均值。
【解决方案2】:

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

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

【讨论】:

  • 有时可能会有圆圈从东向西移动,有时可能是从北向南移动,如果气旋在预报期间移动速度不是很快,有时圆圈可能会非常接近。有时也可能存在非常明确的反曲率,它几乎是 C 模式。这还能用吗?如果是这样,我会开始的!
  • 它应该可以工作。如果您因圈子过多而陷入困境,您可以随时丢弃一些。
  • 我还想指出,有时会没有相交的线,所以不可能找到它们(见第一张图片)
  • 我说的不是圆本身的交点,而是它们的切线。
  • @Matti:我什至没有注意到它告诉你如何找到它。我只是把它作为你所说的一个例子。希望我没有过多地破坏他的乐趣:-)
猜你喜欢
  • 2018-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-25
  • 1970-01-01
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
相关资源
最近更新 更多