在不规则形状的多边形内获取lat / lng

时间:2016-05-17 07:15:23

标签: javascript google-maps

我正在使用此函数来获取不规则形状多边形的质心,但是当多边形是L形时,该点在外面。

function get_polygon_centroid(pts) {
    var first = pts[0], last = pts[pts.length-1];
    if (first.x != last.x || first.y != last.y) pts.push(first);
        var twicearea=0,
        x=0, y=0,
        nPts = pts.length,
        p1, p2, f;
        for ( var i=0, j=nPts-1 ; i<nPts ; j=i++ ) {
            p1 = pts[i]; p2 = pts[j];
            f = p1.x*p2.y - p2.x*p1.y;
            twicearea += f;          
            x += ( p1.x + p2.x ) * f;
            y += ( p1.y + p2.y ) * f;
        }
        f = twicearea * 3;
    return { x:x/f, y:y/f };
}

我的观点是:

(-37.81418,145.13025000000002),
(-37.814330000000005,145.13022),
(-37.813970000000005,145.12727),
(-37.813750000000006,145.12543000000002),
(-37.813680000000005,145.12478000000002),
(-37.81152,145.12517000000003),
(-37.809380000000004,145.12558),
(-37.80951,145.12675000000002),
(-37.80953,145.12685000000002),
(-37.810590000000005,145.12667000000002),
(-37.812630000000006,145.12631000000002),
(-37.81275,145.12628),
(-37.81324,145.13026000000002),
(-37.81326,145.13044000000002),
(-37.81418,145.13025000000002)

如果可能,我想在多边形内部得到一个远离边界的点。

2 个答案:

答案 0 :(得分:0)

一个选项:

  1. 找到你的质心,如果它不在多边形内

    • 计算计算出的质心和多边形顶点之间的所有点

    • 消除多边形内的任何内容。

  2. 使用您的数据集,这将产生7个候选点。

    proof of concept fiddle

    map with markers inside polygon

    代码段

    type
    var geocoder;
    var map;
    
    function initialize() {
      var map = new google.maps.Map(
        document.getElementById("map_canvas"), {
          center: new google.maps.LatLng(37.4419, -122.1419),
          zoom: 13,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        });
      var polygon = new google.maps.Polygon({
        map: map,
        paths: data
      });
      var bounds = new google.maps.LatLngBounds();
      for (var i = 0; i < polygon.getPaths().getAt(0).getLength(); i++) {
        bounds.extend(polygon.getPaths().getAt(0).getAt(i));
      }
      map.fitBounds(bounds);
      var marker = new google.maps.Marker({
        map: map,
        position: get_polygon_centroid(data)
      });
      for (var i = 0; i < polygon.getPaths().getAt(0).getLength(); i++) {
        var polyline = new google.maps.Polyline({
          map: map,
          path: [marker.getPosition(), polygon.getPaths().getAt(0).getAt(i)]
        });
        var length = google.maps.geometry.spherical.computeLength(polyline.getPath());
        var pt = google.maps.geometry.spherical.computeOffset(marker.getPosition(), length / 2,
          google.maps.geometry.spherical.computeHeading(marker.getPosition(), polygon.getPaths().getAt(0).getAt(i)));
        if (google.maps.geometry.poly.containsLocation(pt, polygon)) {
          var candidateMarker = new google.maps.Marker({
            map: map,
            position: pt,
            icon: "http://maps.google.com/mapfiles/ms/micons/blue.png"
          });
        }
      }
    }
    google.maps.event.addDomListener(window, "load", initialize);
    
    function get_polygon_centroid(pts) {
      var first = pts[0],
        last = pts[pts.length - 1];
      if (first.lat != last.lat || first.lng != last.lng) pts.push(first);
      var twicearea = 0,
        x = 0,
        y = 0,
        nPts = pts.length,
        p1, p2, f;
      for (var i = 0, j = nPts - 1; i < nPts; j = i++) {
        p1 = pts[i];
        p2 = pts[j];
        f = p1.lng * p2.lat - p2.lng * p1.lat;
        twicearea += f;
        x += (p1.lng + p2.lng) * f;
        y += (p1.lat + p2.lat) * f;
      }
      f = twicearea * 3;
      return {
        lng: x / f,
        lat: y / f
      };
    }
    
    var data = [{
      lat: -37.81418,
      lng: 145.13025000000002
    }, {
      lat: -37.814330000000005,
      lng: 145.13022
    }, {
      lat: -37.813970000000005,
      lng: 145.12727
    }, {
      lat: -37.813750000000006,
      lng: 145.12543000000002
    }, {
      lat: -37.813680000000005,
      lng: 145.12478000000002
    }, {
      lat: -37.81152,
      lng: 145.12517000000003
    }, {
      lat: -37.809380000000004,
      lng: 145.12558
    }, {
      lat: -37.80951,
      lng: 145.12675000000002
    }, {
      lat: -37.80953,
      lng: 145.12685000000002
    }, {
      lat: -37.810590000000005,
      lng: 145.12667000000002
    }, {
      lat: -37.812630000000006,
      lng: 145.12631000000002
    }, {
      lat: -37.81275,
      lng: 145.12628
    }, {
      lat: -37.81324,
      lng: 145.13026000000002
    }, {
      lat: -37.81326,
      lng: 145.13044000000002
    }, {
      lat: -37.81418,
      lng: 145.13025000000002
    }]
    html,
    body,
    #map_canvas {
      height: 100%;
      width: 100%;
      margin: 0px;
      padding: 0px
    }

答案 1 :(得分:-1)

另一种选择:

  • 使用像Douglas-Peucker这样的东西来简化你的多边形(现在你有额外的点可以产生很多可能的点)
  • 计算多边形
  • 的每组唯一非相邻顶点之间的所有点
  • 消除多边形内的任何内容。

使用您的数据集,这会产生5个候选点。

proof of concept fiddle

map with markers inside polygon

enabled
function initialize() {
  var map = new google.maps.Map(
    document.getElementById("map_canvas"), {
      center: new google.maps.LatLng(37.4419, -122.1419),
      zoom: 13,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    });
  var path = [];
  for (var i = 0; i < data.length; i++) {
    path.push(new google.maps.LatLng(data[i].lat, data[i].lng));
  }
  var simplifiedPath = GDouglasPeucker(path, 20);

  var polygon = new google.maps.Polygon({
    map: map,
    paths: simplifiedPath
  });
  var bounds = new google.maps.LatLngBounds();
  for (var i = 0; i < polygon.getPaths().getAt(0).getLength(); i++) {
    bounds.extend(polygon.getPaths().getAt(0).getAt(i));
  }
  map.fitBounds(bounds);
  var marker = new google.maps.Marker({
    map: map,
    position: get_polygon_centroid(data)
  });
  // check each pair of non-adjacent points
  for (var i = 0; i < polygon.getPaths().getAt(0).getLength() - 2; i++) {
    for (var j = i + 2; j < polygon.getPaths().getAt(0).getLength(); j++) {
      var polyline = new google.maps.Polyline({
        map: map,
        path: [polygon.getPaths().getAt(0).getAt(i), polygon.getPaths().getAt(0).getAt(j)],
        strokeColor: 'blue'
      });
      var length = google.maps.geometry.spherical.computeLength(polyline.getPath());
      var pt = google.maps.geometry.spherical.computeOffset(polygon.getPaths().getAt(0).getAt(i), length / 2,
        google.maps.geometry.spherical.computeHeading(polygon.getPaths().getAt(0).getAt(i), polygon.getPaths().getAt(0).getAt(j)));
      console.log("[i=" + i + ",j=" + j + "] abs(diff)=" + Math.abs(i - j) + " coords:" + pt.toUrlValue(6));
      if (google.maps.geometry.poly.containsLocation(pt, polygon) &&
        !google.maps.geometry.poly.isLocationOnEdge(pt, polygon, 10e-8)) {
        var candidateMarker = new google.maps.Marker({
          map: map,
          position: pt,
          icon: "http://maps.google.com/mapfiles/ms/micons/blue.png",
          title: "[i=" + i + ",j=" + j + "] " + pt.toUrlValue(6)
        });
      } else polyline.setMap(null);
    }
  }
}
google.maps.event.addDomListener(window, "load", initialize);

function get_polygon_centroid(pts) {
  var first = pts[0],
    last = pts[pts.length - 1];
  if (first.lat != last.lat || first.lng != last.lng) pts.push(first);
  var twicearea = 0,
    x = 0,
    y = 0,
    nPts = pts.length,
    p1, p2, f;
  for (var i = 0, j = nPts - 1; i < nPts; j = i++) {
    p1 = pts[i];
    p2 = pts[j];
    f = p1.lng * p2.lat - p2.lng * p1.lat;
    twicearea += f;
    x += (p1.lng + p2.lng) * f;
    y += (p1.lat + p2.lat) * f;
  }
  f = twicearea * 3;
  return {
    lng: x / f,
    lat: y / f
  };
}

var data = [{
    lat: -37.81418,
    lng: 145.13025000000002
  }, {
    lat: -37.814330000000005,
    lng: 145.13022
  }, {
    lat: -37.813970000000005,
    lng: 145.12727
  }, {
    lat: -37.813750000000006,
    lng: 145.12543000000002
  }, {
    lat: -37.813680000000005,
    lng: 145.12478000000002
  }, {
    lat: -37.81152,
    lng: 145.12517000000003
  }, {
    lat: -37.809380000000004,
    lng: 145.12558
  }, {
    lat: -37.80951,
    lng: 145.12675000000002
  }, {
    lat: -37.80953,
    lng: 145.12685000000002
  }, {
    lat: -37.810590000000005,
    lng: 145.12667000000002
  }, {
    lat: -37.812630000000006,
    lng: 145.12631000000002
  }, {
    lat: -37.81275,
    lng: 145.12628
  }, {
    lat: -37.81324,
    lng: 145.13026000000002
  }, {
    lat: -37.81326,
    lng: 145.13044000000002
  }, {
    lat: -37.81418,
    lng: 145.13025000000002
  }]
  /* Stack-based Douglas Peucker line simplification routine 
     returned is a reduced GLatLng array 
     After code by  Dr. Gary J. Robinson,
     Environmental Systems Science Centre,
     University of Reading, Reading, UK
  */

function GDouglasPeucker(source, kink)
  /* source[] Input coordinates in GLatLngs 	*/
  /* kink	in metres, kinks above this depth kept  */
  /* kink depth is the height of the triangle abc where a-b and b-c are two consecutive line segments */
  {
    var n_source, n_stack, n_dest, start, end, i, sig;
    var dev_sqr, max_dev_sqr, band_sqr;
    var x12, y12, d12, x13, y13, d13, x23, y23, d23;
    var F = ((Math.PI / 180.0) * 0.5);
    var index = new Array(); /* aray of indexes of source points to include in the reduced line */
    var sig_start = new Array(); /* indices of start & end of working section */
    var sig_end = new Array();

    /* check for simple cases */

    if (source.length < 3)
      return (source); /* one or two points */

    /* more complex case. initialize stack */

    n_source = source.length;
    band_sqr = kink * 360.0 / (2.0 * Math.PI * 6378137.0); /* Now in degrees */
    band_sqr *= band_sqr;
    n_dest = 0;
    sig_start[0] = 0;
    sig_end[0] = n_source - 1;
    n_stack = 1;

    /* while the stack is not empty  ... */
    while (n_stack > 0) {

      /* ... pop the top-most entries off the stacks */

      start = sig_start[n_stack - 1];
      end = sig_end[n_stack - 1];
      n_stack--;

      if ((end - start) > 1) { /* any intermediate points ? */

        /* ... yes, so find most deviant intermediate point to
               either side of line joining start & end points */

        x12 = (source[end].lng() - source[start].lng());
        y12 = (source[end].lat() - source[start].lat());
        if (Math.abs(x12) > 180.0)
          x12 = 360.0 - Math.abs(x12);
        x12 *= Math.cos(F * (source[end].lat() + source[start].lat())); /* use avg lat to reduce lng */
        d12 = (x12 * x12) + (y12 * y12);

        for (i = start + 1, sig = start, max_dev_sqr = -1.0; i < end; i++) {

          x13 = (source[i].lng() - source[start].lng());
          y13 = (source[i].lat() - source[start].lat());
          if (Math.abs(x13) > 180.0)
            x13 = 360.0 - Math.abs(x13);
          x13 *= Math.cos(F * (source[i].lat() + source[start].lat()));
          d13 = (x13 * x13) + (y13 * y13);

          x23 = (source[i].lng() - source[end].lng());
          y23 = (source[i].lat() - source[end].lat());
          if (Math.abs(x23) > 180.0)
            x23 = 360.0 - Math.abs(x23);
          x23 *= Math.cos(F * (source[i].lat() + source[end].lat()));
          d23 = (x23 * x23) + (y23 * y23);

          if (d13 >= (d12 + d23))
            dev_sqr = d23;
          else if (d23 >= (d12 + d13))
            dev_sqr = d13;
          else
            dev_sqr = (x13 * y12 - y13 * x12) * (x13 * y12 - y13 * x12) / d12; // solve triangle

          if (dev_sqr > max_dev_sqr) {
            sig = i;
            max_dev_sqr = dev_sqr;
          }
        }

        if (max_dev_sqr < band_sqr) { /* is there a sig. intermediate point ? */
          /* ... no, so transfer current start point */
          index[n_dest] = start;
          n_dest++;
        } else {
          /* ... yes, so push two sub-sections on stack for further processing */
          n_stack++;
          sig_start[n_stack - 1] = sig;
          sig_end[n_stack - 1] = end;
          n_stack++;
          sig_start[n_stack - 1] = start;
          sig_end[n_stack - 1] = sig;
        }
      } else {
        /* ... no intermediate points, so transfer current start point */
        index[n_dest] = start;
        n_dest++;
      }
    }

    /* transfer last point */
    index[n_dest] = n_source - 1;
    n_dest++;

    /* make return array */
    var r = new Array();
    for (var i = 0; i < n_dest; i++)
      r.push(source[index[i]]);
    return r;

  }
html,
body,
#map_canvas {
  height: 100%;
  width: 100%;
  margin: 0px;
  padding: 0px
}