0

I am trying to create an utility where user will be able to create inner hole in a polgon. The piece of code is working fine. But I am not able to capture the events while streching or moving the vertices of inner ring.

How to produce: Create a polygon using handler. Draw another polgon inside the polygon. You will see it as hole.

Further select the polygon and strech the point. You will get the alert while streching the outer polygon. I want the same behaviour for the inner polygon (Mean to say trigger event on inner polygon too). What is wrong I am doing here. Please help.

Piece of code below:

var selectedShape;
///Function for selecting a shape
function setSelection(shape) {
  clearSelection();
  selectedShape = shape;
  shape.setEditable(true);
}

//Clear the selection of a polygon.
function clearSelection() {
  if (selectedShape) {
    selectedShape.setEditable(false);
    selectedShape = null;
  }
}

//Check if Polygon is inside Polygon****
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;
  }

//Rewind Co-ords for Creating Hole
  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()));
    }
    
    if (area >= 0 !== !dir)
      ring.reverse();
    return ring;
  }

function initialize() {
    var overlayArr=[];
    var map = new google.maps.Map(document.getElementById('polymap'), {
    zoom: 14,
    center: new google.maps.LatLng(28.631162, 77.213313),
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    disableDefaultUI: true,
    zoomControl: true,
    fullscreenControl: true
    });

    var polyOptions = {
    strokeWeight: 0,
    fillOpacity: 0.45,
    editable: true
    };


      // Creates a drawing manager attached to the map that allows the user to draw
      // markers, lines, and shapes.
    drawingManager = new google.maps.drawing.DrawingManager({
    drawingMode: google.maps.drawing.OverlayType.POLYGON,
    drawingControlOptions: {
    drawingModes: [google.maps.drawing.OverlayType.POLYGON]
    },
    markerOptions: {
    draggable: true
    },
    polylineOptions: {
    editable: true
    },
    rectangleOptions: polyOptions,
    circleOptions: polyOptions,
    polygonOptions: polyOptions,
    map: map
    });


    google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
    if (e.type != google.maps.drawing.OverlayType.MARKER) {

        //New Addition
        var path = e.overlay.getPath().getArray()
        path = rewindRing(path, true);
        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);
        }

        var newShape = e.overlay;
        newShape.type = e.type;
        google.maps.event.addListener(newShape, 'click', function() {setSelection(newShape);});
        }
        setSelection(newShape);
    });

    google.maps.event.addListener(drawingManager, 'polygoncomplete', function (polygon) {
        for (var i = 0; i < selectedShape.getPaths().getLength(); i++) {
                google.maps.event.addListener(selectedShape.getPaths().getAt(i), 'set_at', function() {
                alert("complete called");
                });
                google.maps.event.addListener(selectedShape.getPaths().getAt(i), 'click', function() {
                alert("complete called");
                });
                google.maps.event.addListener(selectedShape.getPaths().getAt(i), 'dragend', function() {
                alert("complete called");
                });
                google.maps.event.addListener(selectedShape.getPaths().getAt(i), 'insert_at', function() {
                alert("complete called");
                });
                google.maps.event.addListener(selectedShape.getPaths().getAt(i), 'remove_at', function() {
                alert("complete called");
                });
                }
            });
}
<div id="polymap" style="height:300px;"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=drawing&callback=initialize&" async defer></script>
Yrll
  • 1,619
  • 2
  • 5
  • 19
Hungry
  • 39
  • 12

1 Answers1

1

You need to add the insert_at, remove_at and set_at event listeners to the inner path after you add it to the outer polygon.

Related questions:

Where the code decides to add it to the outer polygon, add the event listeners of the inner path(s) (in this case it will only be adding a single new path):

if (isPolygonInsidePolygon(e.overlay, overlayArr[i])) {
  found = true;
  var path = e.overlay.getPath().getArray();
  path = rewindRing(path, false);
  var newPathIdx = overlayArr[i].getPaths().getLength(); // index of added path
  overlayArr[i].getPaths().push(new google.maps.MVCArray(path));
  e.overlay.setMap(null);
              
  google.maps.event.addListener(selectedShape.getPaths().getAt(newPathIdx), 'set_at', function() {
    alert("inner path set_at called");
  });
  google.maps.event.addListener(selectedShape.getPaths().getAt(newPathIdx), 'insert_at', function() {
    alert("inner path insert_at called");
  });
  google.maps.event.addListener(selectedShape.getPaths().getAt(newPathIdx), 'remove_at', function() {
    alert("inner path remove_at called");
  });
  break;
}

updated code snippet:

var selectedShape;
///Function for selecting a shape
function setSelection(shape) {
  clearSelection();
  selectedShape = shape;
  shape.setEditable(true);
}

//Clear the selection of a polygon.
function clearSelection() {
  if (selectedShape) {
    selectedShape.setEditable(false);
    selectedShape = null;
  }
}

//Check if Polygon is inside Polygon****
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;
}

//Rewind Co-ords for Creating Hole
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()));
  }

  if (area >= 0 !== !dir)
    ring.reverse();
  return ring;
}

function initialize() {
  var overlayArr = [];
  var map = new google.maps.Map(document.getElementById('polymap'), {
    zoom: 14,
    center: new google.maps.LatLng(28.631162, 77.213313),
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    disableDefaultUI: true,
    zoomControl: true,
    fullscreenControl: true
  });

  var polyOptions = {
    strokeWeight: 0,
    fillOpacity: 0.45,
    editable: true
  };


  // Creates a drawing manager attached to the map that allows the user to draw
  // markers, lines, and shapes.
  drawingManager = new google.maps.drawing.DrawingManager({
    drawingMode: google.maps.drawing.OverlayType.POLYGON,
    drawingControlOptions: {
      drawingModes: [google.maps.drawing.OverlayType.POLYGON]
    },
    markerOptions: {
      draggable: true
    },
    polylineOptions: {
      editable: true
    },
    rectangleOptions: polyOptions,
    circleOptions: polyOptions,
    polygonOptions: polyOptions,
    map: map
  });


  google.maps.event.addListener(drawingManager, 'overlaycomplete', function(e) {
    if (e.type != google.maps.drawing.OverlayType.MARKER) {

      //New Addition
      var path = e.overlay.getPath().getArray()
      path = rewindRing(path, true);
      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);
          var newPathIdx = overlayArr[i].getPaths().getLength();
          overlayArr[i].getPaths().push(new google.maps.MVCArray(path));
          e.overlay.setMap(null);
          console.log("isPolygonInsidePolygon, paths=" + overlayArr[i].getPaths().getLength());
          google.maps.event.addListener(selectedShape.getPaths().getAt(newPathIdx), 'set_at', function() {
            alert("inner path set_at called");
          });
          google.maps.event.addListener(selectedShape.getPaths().getAt(newPathIdx), 'insert_at', function() {
            alert("inner path insert_at called");
          });
          google.maps.event.addListener(selectedShape.getPaths().getAt(newPathIdx), 'remove_at', function() {
            alert("inner path remove_at called");
          });
          break;
        }
      }
      if (!found) {
        overlayArr.push(e.overlay);
      }

      var newShape = e.overlay;
      newShape.type = e.type;
      google.maps.event.addListener(newShape, 'click', function() {
        setSelection(newShape);
      });
    }
    setSelection(newShape);
  });

  google.maps.event.addListener(drawingManager, 'polygoncomplete', function(polygon) {
    console.log("polygoncomplete, paths=" + selectedShape.getPaths().getLength());
    for (var i = 0; i < selectedShape.getPaths().getLength(); i++) {
      google.maps.event.addListener(selectedShape.getPaths().getAt(i), 'set_at', function() {
        alert("set_at called");
      });
      google.maps.event.addListener(selectedShape.getPaths().getAt(i), 'insert_at', function() {
        alert("insert_at called");
      });
      google.maps.event.addListener(selectedShape.getPaths().getAt(i), 'remove_at', function() {
        alert("remove_at called");
      });
    }
  });
}
<div id="polymap" style="height:300px;"></div>
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBIc-PhM9_Uwpjbks0WPvtkKYagOXTk12A&libraries=drawing&callback=initialize&" async defer></script>
geocodezip
  • 158,664
  • 13
  • 220
  • 245
  • how I can remove a specific hole. E.g., hole1.setMap(null). But this makes it polygon. This for specific use case where the already created hole need to be removed on dblClick or using some context menu – Hungry Jul 18 '21 at 17:16
  • That is a new and different question, be sure to provide a [mcve] in that question which demonstrates your issue (although it wouldn't hurt to reference this question for context) – geocodezip Jul 18 '21 at 18:50
  • I have added the new question with complete context [Link](https://stackoverflow.com/questions/68435162/removing-the-specific-hole-in-google-maps-polygon-using-javascript) – Hungry Jul 19 '21 at 04:36
  • I have added a new question specific to the probllem I faced. But has been closed by community. Not able to see any specific reason. Disappointing for me. – Hungry Jul 19 '21 at 19:01