首页 > 解决方案 > 过滤覆盖的孔,然后将其推送到数据层而不重复

问题描述

我在 DrawManager 绘图和将叠加层导出到 GeoJSON 对象时遇到问题。

我可以在 DrawManager 中绘制一个带有孔的多边形,并且覆盖层将显示对象中的孔。问题在于将其导出到 DataLayer 时。

数据层使导出 GeoJSON 变得容易。关于孔:多边形数据层

map.data.add({
    geometry: new google.maps.Data.Polygon([
    outerCoords,
    innerCoords1,//hole
    innerCoords2, //hole
    ]),
});

如何导出 DrawManager 绘图以适应此模式?

我尝试了使用数据层上的倒带路径的解决方案。但是我无法让孔链接到带孔的多边形。它会创建重复项。DrawingManager 显示数据,但我正在努力导出这些数据以使其看起来像绘制的图像。

绘制的图像: 在此处输入图像描述

输出 GeoJSON 数据:

{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[150.3308896484375,-34.242748228904865],[150.0232724609375,-34.555492148137766],[150.764849609375,-34.70465158215243],[151.0779599609375,-34.27452911659509],[150.3308896484375,-34.242748228904865]]]},"properties":{}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[150.3308896484375,-34.242748228904865],[150.0232724609375,-34.555492148137766],[150.764849609375,-34.70465158215243],[151.0779599609375,-34.27452911659509],[150.3308896484375,-34.242748228904865]],[[150.8362607421875,-34.41512862111033],[150.35835546875,-34.51476578284105],[150.53413671875,-34.392467230948675],[150.8362607421875,-34.41512862111033]]]},"properties":{}},{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[150.3308896484375,-34.242748228904865],[150.0232724609375,-34.555492148137766],[150.764849609375,-34.70465158215243],[151.0779599609375,-34.27452911659509],[150.3308896484375,-34.242748228904865]],[[150.44624609375,-34.35619624254908],[150.237505859375,-34.49213141771243],[150.1606015625,-34.47854784880349],[150.369341796875,-34.29722239841581],[150.44624609375,-34.35619624254908]]]},"properties":{}}]}

绘制输出: 图层中的重复项 数据没有附加孔,它正在重新创建 [outer,inner] 数据层。

代码片段:

    // 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,
                    google.maps.drawing.OverlayType.CIRCLE
                ],
            },
        });
        var dataLayer = new google.maps.Data();
        drawingManager.setMap(map);
        var overlayArr = [] //Store all completed objects
        var holeArr = [] //Keep track of holes to build polygon with data layer
        google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
            if (e.type === 'polygon') {
                console.log("overlaycomplete start overlayArr.length=" + overlayArr.length);
                var path = e.overlay.getPath().getArray()
                path = rewindRing(path, true);
                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))
                        dataLayer.add(new google.maps.Data.Feature({
                            geometry: new google.maps.Data.Polygon([overlayArr[i].getPath().getArray(), path]) //Store holes as the secondary "outer" to save to GeoJSON
                        }));
                        holeArr.push(overlayArr[i]);
                        e.overlay.setMap(null); //make hole
                        break;
                    }
                }
                if (!found) {
                    overlayArr.push(e.overlay);
                    //This is a duplicate object in the datalayer after a hole is drawn -- how to remove if hole is drawn?
                    dataLayer.add(new google.maps.Data.Feature({
                        geometry: new google.maps.Data.Polygon([e.overlay.getPath().getArray()]) //No holes add normal
                    }));
                    found = false;
                }
            }
            if (e.type === 'circle') {
                dataLayer.add(new google.maps.Data.Feature({
                    properties: {
                        radius: e.overlay.getRadius()
                    },
                    geometry: new google.maps.Data.Point(e.overlay.getCenter())
                }));
            }
        });
    
        google.maps.event.addDomListener(document.getElementById('save'), 'click', function() {
            dataLayer.toGeoJson(function(obj) {
                document.getElementById('geojson').innerHTML = JSON.stringify(obj);
            });
        })
    
        //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;
        }
    
        // 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>
  <input id="save" value="save" type="button" />
  <div id="geojson"></div>
    <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>

标签: javascriptarraysgoogle-maps-api-3

解决方案


我的建议是在需要导出多边形之前不要将它们添加到 DataLayer。在绘制多边形时删除将多边形添加到 DataLayer 的代码,然后使您的导出函数如下所示:

  google.maps.event.addDomListener(document.getElementById('export'), 'click', function() {
    for (var i=0; i<overlayArr.length;i++) {
      // for each polygon drawn, get its paths
      var paths = [];
      for (var j=0; j<overlayArr[i].getPaths().getLength();j++) {
        var path = [];
        for (k=0; k<overlayArr[i].getPaths().getAt(j).getLength(); k++) {
          path.push(overlayArr[i].getPaths().getAt(j).getAt(k));
        }
        paths.push(path);
      }
      // create a Data.Polygon
      map.data.add({
        geometry: new google.maps.Data.Polygon(paths)
      });    
    }
    // export the GeoJson from the Data Layer
    map.data.toGeoJson(function(geoJson){
      console.log(geoJson);
      document.getElementById('geoJson').innerHTML = JSON.stringify(geoJson);
    });
  });

概念证明小提琴

带有两个孔的多边形的屏幕截图

// 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,
                    google.maps.drawing.OverlayType.CIRCLE
                ],
            },
        });
        var dataLayer = new google.maps.Data();
        drawingManager.setMap(map);
        var overlayArr = [] //Store all completed objects
        var holeArr = [] //Keep track of holes to build polygon with data layer
        google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
            if (e.type === 'polygon') {
                console.log("overlaycomplete start overlayArr.length=" + overlayArr.length);
                var path = e.overlay.getPath().getArray()
                path = rewindRing(path, true);
                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))
                        dataLayer.add(new google.maps.Data.Feature({
                            geometry: new google.maps.Data.Polygon([overlayArr[i].getPath().getArray(), path]) //Store holes as the secondary "outer" to save to GeoJSON
                        }));
                        holeArr.push(overlayArr[i]);
                        e.overlay.setMap(null); //make hole
                        break;
                    }
                }
                if (!found) {
                    overlayArr.push(e.overlay);
                    //This is a duplicate object in the datalayer after a hole is drawn -- how to remove if hole is drawn?
                    dataLayer.add(new google.maps.Data.Feature({
                        geometry: new google.maps.Data.Polygon([e.overlay.getPath().getArray()]) //No holes add normal
                    }));
                    found = false;
                }
            }
            if (e.type === 'circle') {
                dataLayer.add(new google.maps.Data.Feature({
                    properties: {
                        radius: e.overlay.getRadius()
                    },
                    geometry: new google.maps.Data.Point(e.overlay.getCenter())
                }));
            }
        });
    
        google.maps.event.addDomListener(document.getElementById('export'), 'click', function() {
            dataLayer.toGeoJson(function(obj) {
                document.getElementById('geoJson').innerHTML = JSON.stringify(obj);
            });
        })
    
        //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;
        }
    
        // 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: 90%;
}

/* 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>
    <input id="export" value="export" type="button" />
    <div id="map"></div>
    <div id="geoJson"></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>


推荐阅读