首页 > 解决方案 > 使用 DrawingManager 在 Google 地图多边形中绘制孔

问题描述

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

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 个农场可能有多个我需要消除的障碍物。

标签: javascriptgoogle-maps-api-3

解决方案


要打孔,如果外多边形是逆时针的,内多边形需要顺时针(缠绕方向),反之亦然。根据文档

要在多边形内创建一个空白区域,您需要创建两条路径,一条在另一条内部。要创建孔,定义内部路径的坐标必须与定义外部路径的坐标顺序相反。例如,如果外部路径的坐标是顺时针方向,那么内部路径必须是逆时针方向。

相关问题:

一种选择:

  1. 创建一个函数来控制缠绕方向。下面是来自https://github.com/mapbox/geojson-rewind/blob/main/index.js的修改(获取google.maps.LatLng对象数组)版本rewindRing
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);
}

概念证明小提琴

由 DrawingManager 绘制的带有孔的多边形的屏幕截图

活生生的例子

代码片段:

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


推荐阅读