Reputation: 37
So far I was keeping a list of drawn objects into an array
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
});
Containing all of the objects I then need to generate a GeoJSON but first if a polygon has a polygon inside of it--then it must be drawn as a hole
//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;
}
Generate the GeoJSON of each object without checking for collisions:
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;
};
How can I combine the google.maps.event.addListener(drawingManager, 'overlaycomplete',...)
listener to detect collisions and make them generate geoJSON with holes?
I'm basically drawing farm fields on a map and want to put holes in the obstacles that are not farm-able so 1 farm may have multiple obstacles I need to cut out.
Upvotes: 0
Views: 1727
Reputation: 161334
To make holes, if the outer polygon is counter-clockwise, the inner ones need to be clockwise (winding direction), and vice versa. Per 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.
Related question:
One option:
google.maps.LatLng
objects) version of rewindRing
from https://github.com/mapbox/geojson-rewind/blob/main/index.jsfunction 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;
}
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);
}
code snippet:
// 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>
Upvotes: 4