多个圆圈 - >一个多边形?

时间:2010-04-10 19:12:00

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

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

Map with multiple circles

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

Correct map http://www.pcwp.com/images2009/ui-2.gif

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

2 个答案:

答案 0 :(得分:8)

您可能需要考虑通过以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 2 http://img188.imageshack.us/img188/5687/mp2a.png

如果没有原始圆圈周围的黑色笔触,这就是它的样子:

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>

答案 1 :(得分:4)

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

如果您的圆圈不像第一篇文章中那样整洁(多次重叠,圈内圆圈等),这可能会破坏,但这只是一个开始。