7

I would like to be able to draw a linestring and to be able to undo the last drawn point when the key ESC is pressed.

I used that code when I first wrote my application :

  // create the interaction
  draw_interaction = new ol.interaction.Draw({
    source: vector_layer.getSource(),
    type: /** @type {ol.geom.GeometryType} */ ($geom_type.val())
  });
    // add it to the map
  map.addInteraction(draw_interaction);

  // when a new feature has been drawn...
  draw_interaction.on('drawstart', function(evt) {
    var feature = evt.feature;
    var geom = feature.getGeometry();
    document.addEventListener('keyup', function() {
      if (event.keyCode === 27) {
        if (geom.getType() === "LineString") {
          var coords = geom.getCoordinates();
          var len = coords.length;
          console.log("undo");
          if (len > 1) {
           geom.setCoordinates(geom.getCoordinates().slice(0, len - 1));
          }
        }
      }
    });
  });

You can see a live version on codepen following that link:

http://codepen.io/anon/pen/waPXyG

My problem is when I upgraded from 3.5.0 to 3.6.0, the behaviour changed :

http://codepen.io/anon/pen/LVOrrq

As you can see on the live example, when undoing (pressing ESC) it seems to be working but when you want to add a new point to the linestring, the points I removed are back in place. Looks like a caching behaviour as been added to 3.6.0

I hope someone can help me, it's either a regression or a api modification that I did not notice in the change log.

2
  • My guess is that you have to rethink your logic. For example, if you add a keyup listener every drawstart this becomes unmanaged. Jun 25, 2015 at 10:28
  • In my code, I remove the event listener on drawend event. I did not copy all my code for the sake of simplicity in the codepen exemple. I am glad if you could provide me with a better design, I guess I am not the only one who needs to manage undos in my webapp Jun 25, 2015 at 18:25

3 Answers 3

9

Since OpenLayers 3.9.0, this is as simple as using the removeLastPoint method of the DrawInteraction. So you can do something like this:

document.addEventListener('keydown', function(e) {
    if (e.which == 27)
        draw_interaction.removeLastPoint()
});
5

Since v3.6.0, a geometryFunction is used to return the geometry resulting from drawing with ol.interaction.Draw. This geometryFunction is where you modify the geometry during drawing. So what you can do is set an undo flag to true when the user hits the Esc key. In the geometryFunction you change the coordinates when the flag was set:

var undo = false; // set this to true in the Esc key handler
var draw = new ol.interaction.Draw({
  // ...
  geometryFunction: function(coords, geom) {
    if (!geom) {
      geom = new ol.geom.LineString(null);
    }
    if (undo) {
      if (coords.length > 1) {
        coords.pop();
      }
      undo = false;
    }
    geom.setCoordinates(coords);
    return geom;
  }
});
8
  • That's right. This is the correct way to do this. But could you explain me why this plunker doesn't works. I'm just trying an immediate (visual) to your solution. Jun 29, 2015 at 20:26
  • @ahocevar: I've seen that function at lunch time and was goingto give it a try, you saved me some time, thank you for that :) Jun 29, 2015 at 20:33
  • @ahocevar: your function did the jopb, whoever I have a little question, when using a polygon shape, I have coords[0][0] = Array[2] (0: 571349.7961789053 1: 5722147.431410865) instead of coords[0] = Array[2] is it an expected behaviour? Jun 29, 2015 at 21:18
  • Yes, this is expected. A polygon is made up of rings. One outer ring and zero or more inner rings.
    – ahocevar
    Jun 30, 2015 at 21:09
  • 1
    The code was working fine, but since I switched to ol3 3.9.0, pressing the esc key does nothing if the mouse does not move... any idea? Oct 5, 2015 at 15:13
0

I've made some tests and this can be done with something like this:

Set a custom listener to your draw interaction.

draw.set('escKey', '');

Set a global variable if drawing start, and store the created feature:

draw.on('drawstart', function(evt){
    drawing = true;
    drawing_feature = evt.feature;
});

Set a keydown listener to the document and if drawing is active, fire your custom event:

var keydown = function(evt){
    var charCode = (evt.which) ? evt.which : evt.keyCode;
    if (charCode === 27 && drawing === true){ //esc key
        //dispatch event
        draw.set('escKey', Math.random());
    }
};
document.addEventListener('keydown', keydown, false);

And then slice the lineString coordinates:

draw.on('change:escKey', function(evt){

    var geom = drawing_feature.getGeometry();
    var coords = geom.getCoordinates();
    var len = coords.length;
    if (len > 1) {
        var new_coordinates = coords.slice(0, len - 1);

        geom.setCoordinates(new_coordinates); //this is just visual
        //this (target.e) is the equivalent of this.sketchCoords_ in debug mode
        evt.target.e = new_coordinates;
    }
});

Now, notice this evt.target.e, or this.sketchCoords_ in ol-debug.js. This is a temporary/internal array of coords, perhaps, there's a better way to do this. I've tried a lot of others options and this was the only way.

Maybe a core developer can show us a better approach.

Oh yes, a working demo.

2
  • That's really nice to have dedicated some of your precious time on this. Appreciate it! I'm curious to see if a core dev can give some input as well :) Jun 28, 2015 at 18:26
  • 2
    You should not rely on properties like evt.target.e, because e is a symbol of the minified build and is neither API nor stable.
    – ahocevar
    Jun 29, 2015 at 19:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.