使用 DrawingManager 在 Google 地图多边形中绘制孔

Drawing holes in Google Maps Polygons with DrawingManager

到目前为止,我将绘制的对象列表保存到一个数组中

var overlayArr = [] //Store all completed objects
var formattedWithHoles = [] //Store converted collisions
google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
      overlayArr.push(e.overlay);
      overlayArr.forEach(isPolygonInsidePolygon) //Doesn't work right
});

包含所有对象然后我需要生成一个 GeoJSON 但首先如果一个多边形内部有一个多边形——那么它必须被绘制为一个洞

//Helper function
function isPolygonInsidePolygon(innerPolygon, outerPolygon) {
  var points = innerPolygon.getPath().getArray();

  for( var i = 0; i < points.length; i++ ){
    if(!google.maps.geometry.poly.containsLocation( points[i], outerPolygon) ){ 
      return false; 
    }
  }
  //formattedWithHoles.push(...); //Use inner and outer polygon data class?
  return true; 
}

在不检查碰撞的情况下生成每个对象的 GeoJSON:

google.maps.Polygon.prototype.getGeoJSON = function()  {
//GeoJSON format
let geoJSON = {
    type: 'Feature',
    geometry: {
        type: 'Polygon', //MultiPolygon for holes?
        coordinates: []
    },
    properties: {}
};

    let paths = this.getPaths().getArray();

    for (var path of paths)  {
        let pathArray = [];
        let points = path.getArray();
        let firstPoint = false;

        for (var point of points)  {
            if (firstPoint === false)  {
                firstPoint = point;
            }
            pathArray.push([point.lng(), point.lat()])
        }

        pathArray.push([firstPoint.lng(), firstPoint.lat()]);
        geoJSON.geometry.coordinates.push(pathArray);
    }
    //console.log(geoJSON);
    return geoJSON;
};

如何结合 google.maps.event.addListener(drawingManager, 'overlaycomplete',...) 侦听器来检测碰撞并使它们生成带有漏洞的 geoJSON?

我基本上是在地图上画农田,想在无法耕种的障碍物上打洞,所以 1 个农场可能有多个障碍物,我需要消除。

打孔时,如果外边是逆时针,内边需要顺时针(缠绕方向),反之亦然。每 the documentation:

To create an empty area within a polygon, you need to create two paths, one inside the other. To create the hole, the coordinates defining the inner path must be in the opposite order to those defining the outer path. For example, if the coordinates of the outer path are in clockwise order then the inner path must be counter-clockwise.

相关问题:

一个选项:

  1. 创建一个函数来控制缠绕方向。下面是来自 https://github.com/mapbox/geojson-rewind/blob/main/index.js
  2. rewindRing 的修改版本(获取 google.maps.LatLng 对象的数组)
function rewindRing(ring, dir) {
    var area = 0;
    for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
      area += ((ring[i].lng() - ring[j].lng()) * (ring[j].lat() + ring[i].lat()));
    }
    console.log("area="+area+" dir="+dir);
    if (area >= 0 !== !dir) 
      ring.reverse();
    return ring;
  }
  1. 当您在另一个多边形中找到一个多边形时,反转其缠绕方向:
var found = false;
for (var i = 0; i < overlayArr.length; i++) {
  if (isPolygonInsidePolygon(e.overlay, overlayArr[i])) {
    found = true;
    var path = e.overlay.getPath().getArray();
    path = rewindRing(path, false);
    overlayArr[i].getPaths().push(new google.maps.MVCArray(path));
    e.overlay.setMap(null);
    break;
  }
}
if (!found) {
  console.log("!found");
  overlayArr.push(e.overlay);
}

proof of concept fiddle

Live example

代码片段:

// This example requires the Drawing library. Include the libraries=drawing
// parameter when you first load the API. For example:
// <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBIwzALxUPNbatRBj3Xi1Uhp0fFzwWNBkE&libraries=drawing">
function initMap() {
  const map = new google.maps.Map(document.getElementById("map"), {
    center: {
      lat: -34.397,
      lng: 150.644
    },
    zoom: 8,
  });
  const drawingManager = new google.maps.drawing.DrawingManager({
    drawingMode: google.maps.drawing.OverlayType.POLYGON,
    drawingControl: true,
    drawingControlOptions: {
      position: google.maps.ControlPosition.TOP_CENTER,
      drawingModes: [
        google.maps.drawing.OverlayType.POLYGON,
      ],
    },
  });
  drawingManager.setMap(map);
  var overlayArr = [] //Store all completed objects
  var formattedWithHoles = [] //Store converted collisions
  google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
    var path = e.overlay.getPath().getArray()
    path = rewindRing(path, true);
    var newPoly = new google.maps.Polygon({
      path: path,
    })
    var found = false;
    for (var i = 0; i < overlayArr.length; i++) {
      if (isPolygonInsidePolygon(e.overlay, overlayArr[i])) {
        found = true;
        var path = e.overlay.getPath().getArray();
        path = rewindRing(path, false);
        overlayArr[i].getPaths().push(new google.maps.MVCArray(path));
        e.overlay.setMap(null);
        break;
      }
    }
    if (!found) {
      overlayArr.push(e.overlay);
    }
  });

  //Helper function
  function isPolygonInsidePolygon(innerPolygon, outerPolygon) {
    var points = innerPolygon.getPath().getArray();

    for (var i = 0; i < points.length; i++) {
      if (!google.maps.geometry.poly.containsLocation(points[i], outerPolygon)) {
        return false;
      }
    }
    return true;
  }

  google.maps.Polygon.prototype.getGeoJSON = function() {
    //GeoJSON format
    let geoJSON = {
      type: 'Feature',
      geometry: {
        type: 'Polygon', //MultiPolygon for holes?
        coordinates: []
      },
      properties: {}
    };

    let paths = this.getPaths().getArray();

    for (var path of paths) {
      let pathArray = [];
      let points = path.getArray();
      let firstPoint = false;

      for (var point of points) {
        if (firstPoint === false) {
          firstPoint = point;
        }
        pathArray.push([point.lng(), point.lat()])
      }

      pathArray.push([firstPoint.lng(), firstPoint.lat()]);
      geoJSON.geometry.coordinates.push(pathArray);
    }
    //console.log(geoJSON);
    return geoJSON;
  };

  // from https://github.com/mapbox/geojson-rewind/blob/main/index.js
  function rewindRing(ring, dir) {
    var area = 0;
    for (var i = 0, len = ring.length, j = len - 1; i < len; j = i++) {
      area += ((ring[i].lng() - ring[j].lng()) * (ring[j].lat() + ring[i].lat()));
    }
    console.log("area=" + area + " dir=" + dir);
    if (area >= 0 !== !dir)
      ring.reverse();
    return ring;
  }
}
/* Always set the map height explicitly to define the size of the div
       * element that contains the map. */

#map {
  height: 100%;
}


/* Optional: Makes the sample page fill the window. */

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}
<!DOCTYPE html>
<html>

<head>
  <title>Drawing Tools</title>
  <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
  <!-- jsFiddle will insert css and js -->
</head>

<body>
  <div id="map"></div>

  <!-- Async script executes immediately and must be after any DOM elements used in callback. -->
  <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback=initMap&libraries=drawing&v=weekly" async></script>
</body>

</html>