Google Maps v3 - Delete vertex on Polygon
Asked Answered
T

6

29

Google Maps has the Drawing library to draw Polylines and Polygons and other things.

Example of this functionality here: http://gmaps-samples-v3.googlecode.com/svn-history/r282/trunk/drawing/drawing-tools.html

I want, when drawing and editing the polygon, to be able to delete one point/vertex on the path. The API docs haven't seemed to hint at anything.

Tartlet answered 12/1, 2012 at 7:15 Comment(1)
The link does not work anymore.Distant
W
28

This is currently an outstanding feature request (acknowledged by Google), issue 3760.

Here's my solution: http://jsbin.com/ajimur/10. It uses a function that adds a delete button to the passed in polygon (below the undo button).


Alternatively, someone suggested this approach: right-click to delete closest vertex, which works fine but is somewhat lacking in UI finesse. I built on the code from the link to check if the click was inside (or within 1 pixel of) the node - in a JSBin here: http://jsbin.com/ajimur/.

EDIT: as Amr Bekhit pointed out - this approach is currently broken, as the events need to be attached to the polygon.

Wachter answered 14/2, 2012 at 15:41 Comment(10)
Your demo page works for me in FF, IE and Chrome but not in Opera which seems to override the rightclick event. But Thx.Marivelmariya
Right - Opera can be a bit mean in what it allows scripts to do by default. Does this help: groups.google.com/a/googleproductforums.com/forum/…Wachter
Not to sound like expertsexchange or anything, but +1 me if it was helpful! :)Wachter
I've been using the "right click to delete" method for a while now and it's been working fine. However, it looks like a recent Google maps update has stopped this from working. I initially thought it was my code, but both your examples have stopped working too...annoying!Asbestosis
I posted about this issue here (code.google.com/p/gmaps-api-issues/issues/detail?id=4112) and fortunately, have received a response. It looks like the editing handles now generate click events that are attached to the polygon itself. So, in order to modify the two samples in the answer, change the polygon's clickable attribute to true and attach the right click event listener to the polygon, not the map.Asbestosis
@AmrBekhit my first sample (the /10) seems to still be working - but the right-click to delete one isn't. I'll edit my answer for now until I have chance to fix the example - unless you have a bin with the fix?Wachter
It's been such a long time since I looked at this! The main issue was that I wasn't specifying the API version when linking the Google maps js file, so it was always pulling the latest version, which could break things. Once I specified the version (using the v=x.x parameter), I could fix the problem using that version and leave it at that. Easiest thing for your demos will probably be to specify one of the old API versions.Asbestosis
@AmrBekhit Sure, but it seems like pegging the version for one of these samples wouldn't be very productive as people writing this for the first time would always want to write against the latest version of the maps API (I should think!) It feels more correct to me to say: 'you could do something like this in the latest version,' rather than: 'I used to do this and it worked' ;)Wachter
In this example jsbin.com/ajimur/10 the polygon is already completed and we can undo vertex when polygon point is changed. But my requirement is to undo the last drawn vertex or point "during when I am drawing" polygon (Not already drawn polygon). For example "i am drawing polygon and by mistake I put point on wrong place and I want to undo it". I searched a lot but cannot find this feature. please help.Jeanniejeannine
@shiv I have not attempted to solve undoing the addition of a point when first drawing a shape. Sorry.Wachter
P
43

Google Maps now provides a "PolyMouseEvent" callback object on events that are triggered from a Polygon or Polyline.

To build on the other answers which suggested a solution involving a right click, all you would need to do is the following in the latest versions of the V3 API:

// this assumes `my_poly` is an normal google.maps.Polygon or Polyline
var deleteNode = function(mev) {
  if (mev.vertex != null) {
    my_poly.getPath().removeAt(mev.vertex);
  }
}
google.maps.event.addListener(my_poly, 'rightclick', deleteNode);

You'll notice that any complex calculations on whether or not we are near the point are no longer necesary, as the Google Maps API is now telling us which vertex we've clicked on.

Note: this will only work while the Polyline/Polygon is in edit mode. (Which is when the vertices you might want to delete are visible.)

As a final thought, you could consider using a click or double click event instead. "Click" is smart enough to not trigger on a drag, though using a single click trigger might still surprise some of your users.

Phung answered 5/7, 2012 at 17:4 Comment(3)
You can switch my_poly in the callback for this. I went with click protected with a confirm dialog... I don't think users would guess to right click (context menu) or double click (zoom), but clicking is natural. Thanks!Alyciaalyda
This doesn't work properly for polygons that have more than one path, such as donuts. See my answer for code that correctly handles multiple paths.Elbe
I agree with with @AdrianSchneider, click+dialog seems like a better UX optionCredenza
W
28

This is currently an outstanding feature request (acknowledged by Google), issue 3760.

Here's my solution: http://jsbin.com/ajimur/10. It uses a function that adds a delete button to the passed in polygon (below the undo button).


Alternatively, someone suggested this approach: right-click to delete closest vertex, which works fine but is somewhat lacking in UI finesse. I built on the code from the link to check if the click was inside (or within 1 pixel of) the node - in a JSBin here: http://jsbin.com/ajimur/.

EDIT: as Amr Bekhit pointed out - this approach is currently broken, as the events need to be attached to the polygon.

Wachter answered 14/2, 2012 at 15:41 Comment(10)
Your demo page works for me in FF, IE and Chrome but not in Opera which seems to override the rightclick event. But Thx.Marivelmariya
Right - Opera can be a bit mean in what it allows scripts to do by default. Does this help: groups.google.com/a/googleproductforums.com/forum/…Wachter
Not to sound like expertsexchange or anything, but +1 me if it was helpful! :)Wachter
I've been using the "right click to delete" method for a while now and it's been working fine. However, it looks like a recent Google maps update has stopped this from working. I initially thought it was my code, but both your examples have stopped working too...annoying!Asbestosis
I posted about this issue here (code.google.com/p/gmaps-api-issues/issues/detail?id=4112) and fortunately, have received a response. It looks like the editing handles now generate click events that are attached to the polygon itself. So, in order to modify the two samples in the answer, change the polygon's clickable attribute to true and attach the right click event listener to the polygon, not the map.Asbestosis
@AmrBekhit my first sample (the /10) seems to still be working - but the right-click to delete one isn't. I'll edit my answer for now until I have chance to fix the example - unless you have a bin with the fix?Wachter
It's been such a long time since I looked at this! The main issue was that I wasn't specifying the API version when linking the Google maps js file, so it was always pulling the latest version, which could break things. Once I specified the version (using the v=x.x parameter), I could fix the problem using that version and leave it at that. Easiest thing for your demos will probably be to specify one of the old API versions.Asbestosis
@AmrBekhit Sure, but it seems like pegging the version for one of these samples wouldn't be very productive as people writing this for the first time would always want to write against the latest version of the maps API (I should think!) It feels more correct to me to say: 'you could do something like this in the latest version,' rather than: 'I used to do this and it worked' ;)Wachter
In this example jsbin.com/ajimur/10 the polygon is already completed and we can undo vertex when polygon point is changed. But my requirement is to undo the last drawn vertex or point "during when I am drawing" polygon (Not already drawn polygon). For example "i am drawing polygon and by mistake I put point on wrong place and I want to undo it". I searched a lot but cannot find this feature. please help.Jeanniejeannine
@shiv I have not attempted to solve undoing the addition of a point when first drawing a shape. Sorry.Wachter
P
21

I found Sean's code very simple and helpful. I just added a limiter to stop deleting when the user has only 3 nodes left. Without it, the user can get down to just one node, and can't edit anymore:

my_poly.addListener('rightclick', function(mev){
    if (mev.vertex != null && this.getPath().getLength() > 3) {
        this.getPath().removeAt(mev.vertex);
    }
});
Pileous answered 21/1, 2013 at 15:22 Comment(2)
Having 2 nodes left is still ok because there is that faded node between them to create a new one. But thanks for the hint, I used it.Peregrinate
Elegant solution! One change needed. Polygons have vertices+1 points in the path because they need an extra point to close the polygon. So change the 3 to a 4. That works for me.Randee
E
11

I ran into situations where I needed to delete nodes from polygons that contained multiple paths. Here's a modification of Sean's and Evil's code:

shape.addListener('rightclick', function(event){
  if(event.path != null && event.vertex != null){
    var path = this.getPaths().getAt(event.path);
    if(path.getLength() > 3){
      path.removeAt(event.vertex);
    }
  }
});
Elbe answered 17/12, 2013 at 22:43 Comment(1)
Thanks! This is a lifesaver (:Frontpage
D
2

Just thought I'd contribute because I was looking for a solution for this too, here's my implementation:

if (m_event.hasOwnProperty('edge') && m_event.edge >= 0 &&
GeofenceService.polygon.getPath().getLength() > 3) {
    GeofenceService.polygon.getPath().removeAt(m_event.edge);
    return;
}

if (m_event.hasOwnProperty('vertex') && m_event.vertex >= 0 &&
GeofenceService.polygon.getPath().getLength() > 3) {
    GeofenceService.polygon.getPath().removeAt(m_event.vertex);
    return;
}

This allows for handling deletion of vertex nodes AND edge nodes, and maintains a minimum of a triangle formation polygon at all times by checking the path length > 3.

Delk answered 23/8, 2013 at 0:55 Comment(0)
F
1

2020 Update

Google provides a working demo of this in their documentation which demonstrates how a to delete a vertex, or a point on the line, by right-clicking on a vertex to show a "Delete" menu.

Check out Deleting a Vertex

Deleting a Vertex

And the code for completeness (see their Github repo);

function initialize() {
  const mapOptions = {
    zoom: 3,
    center: new google.maps.LatLng(0, -180),
    mapTypeId: "terrain",
  };
  const map = new google.maps.Map(document.getElementById("map"), mapOptions);
  const flightPlanCoordinates = [
    new google.maps.LatLng(37.772323, -122.214897),
    new google.maps.LatLng(21.291982, -157.821856),
    new google.maps.LatLng(-18.142599, 178.431),
    new google.maps.LatLng(-27.46758, 153.027892),
  ];
  const flightPath = new google.maps.Polyline({
    path: flightPlanCoordinates,
    editable: true,
    strokeColor: "#FF0000",
    strokeOpacity: 1.0,
    strokeWeight: 2,
    map: map,
  });

  /**
   * A menu that lets a user delete a selected vertex of a path.
   */
  class DeleteMenu extends google.maps.OverlayView {
    constructor() {
      super();
      this.div_ = document.createElement("div");
      this.div_.className = "delete-menu";
      this.div_.innerHTML = "Delete";
      const menu = this;
      google.maps.event.addDomListener(this.div_, "click", () => {
        menu.removeVertex();
      });
    }
    onAdd() {
      const deleteMenu = this;
      const map = this.getMap();
      this.getPanes().floatPane.appendChild(this.div_);
      // mousedown anywhere on the map except on the menu div will close the
      // menu.
      this.divListener_ = google.maps.event.addDomListener(
        map.getDiv(),
        "mousedown",
        (e) => {
          if (e.target != deleteMenu.div_) {
            deleteMenu.close();
          }
        },
        true
      );
    }
    onRemove() {
      if (this.divListener_) {
        google.maps.event.removeListener(this.divListener_);
      }
      this.div_.parentNode.removeChild(this.div_);
      // clean up
      this.set("position", null);
      this.set("path", null);
      this.set("vertex", null);
    }
    close() {
      this.setMap(null);
    }
    draw() {
      const position = this.get("position");
      const projection = this.getProjection();

      if (!position || !projection) {
        return;
      }
      const point = projection.fromLatLngToDivPixel(position);
      this.div_.style.top = point.y + "px";
      this.div_.style.left = point.x + "px";
    }
    /**
     * Opens the menu at a vertex of a given path.
     */
    open(map, path, vertex) {
      this.set("position", path.getAt(vertex));
      this.set("path", path);
      this.set("vertex", vertex);
      this.setMap(map);
      this.draw();
    }
    /**
     * Deletes the vertex from the path.
     */
    removeVertex() {
      const path = this.get("path");
      const vertex = this.get("vertex");

      if (!path || vertex == undefined) {
        this.close();
        return;
      }
      path.removeAt(vertex);
      this.close();
    }
  }
  const deleteMenu = new DeleteMenu();
  google.maps.event.addListener(flightPath, "rightclick", (e) => {
    // Check if click was on a vertex control point
    if (e.vertex == undefined) {
      return;
    }
    deleteMenu.open(map, flightPath.getPath(), e.vertex);
  });
}
Freeze answered 1/10, 2020 at 7:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.