diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index 463e4da8da..5471601651 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -4,6 +4,7 @@ goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.events'); goog.require('goog.events.Event'); +goog.require('goog.events.EventType'); goog.require('goog.functions'); goog.require('ol.Collection'); goog.require('ol.CollectionEventType'); @@ -168,6 +169,14 @@ ol.interaction.Modify = function(options) { */ this.snappedToVertex_ = false; + /** + * Indicate whether the interaction is currently changing a feature's + * coordinates. + * @type {boolean} + * @private + */ + this.changingFeature_ = false; + /** * @type {Array} * @private @@ -235,6 +244,48 @@ ol.interaction.Modify.prototype.addFeature_ = function(feature) { if (!goog.isNull(map)) { this.handlePointerAtPixel_(this.lastPixel_, map); } + goog.events.listen(feature, goog.events.EventType.CHANGE, + this.handleFeatureChange_, false, this); +}; + + +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Modify.prototype.removeFeature_ = function(feature) { + this.removeFeatureSegmentData_(feature); + // Remove the vertex feature if the collection of canditate features + // is empty. + if (!goog.isNull(this.vertexFeature_) && + this.features_.getLength() === 0) { + this.overlay_.getSource().removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; + } + goog.events.unlisten(feature, goog.events.EventType.CHANGE, + this.handleFeatureChange_, false, this); +}; + + +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Modify.prototype.removeFeatureSegmentData_ = function(feature) { + var rBush = this.rBush_; + var /** @type {Array.} */ nodesToRemove = []; + rBush.forEach( + /** + * @param {ol.interaction.SegmentDataType} node RTree node. + */ + function(node) { + if (feature === node.feature) { + nodesToRemove.push(node); + } + }); + for (var i = nodesToRemove.length - 1; i >= 0; --i) { + rBush.remove(nodesToRemove[i]); + } }; @@ -259,28 +310,26 @@ ol.interaction.Modify.prototype.handleFeatureAdd_ = function(evt) { }; +/** + * @param {goog.events.Event} evt Event. + * @private + */ +ol.interaction.Modify.prototype.handleFeatureChange_ = function(evt) { + if (!this.changingFeature_) { + var feature = /** @type {ol.Feature} */ (evt.target); + this.removeFeature_(feature); + this.addFeature_(feature); + } +}; + + /** * @param {ol.CollectionEvent} evt Event. * @private */ ol.interaction.Modify.prototype.handleFeatureRemove_ = function(evt) { - var feature = evt.element; - var rBush = this.rBush_; - var i, nodesToRemove = []; - rBush.forEachInExtent(feature.getGeometry().getExtent(), function(node) { - if (feature === node.feature) { - nodesToRemove.push(node); - } - }); - for (i = nodesToRemove.length - 1; i >= 0; --i) { - rBush.remove(nodesToRemove[i]); - } - // There remains only vertexFeature… - if (!goog.isNull(this.vertexFeature_) && - this.features_.getLength() === 0) { - this.overlay_.getSource().removeFeature(this.vertexFeature_); - this.vertexFeature_ = null; - } + var feature = /** @type {ol.Feature} */ (evt.element); + this.removeFeature_(feature); }; @@ -585,7 +634,7 @@ ol.interaction.Modify.handleDragEvent_ = function(evt) { break; } - geometry.setCoordinates(coordinates); + this.setGeometryCoordinates_(geometry, coordinates); } this.createOrUpdateVertexFeature_(vertex); }; @@ -768,7 +817,7 @@ ol.interaction.Modify.prototype.insertVertex_ = function(segmentData, vertex) { return; } - geometry.setCoordinates(coordinates); + this.setGeometryCoordinates_(geometry, coordinates); var rTree = this.rBush_; goog.asserts.assert(goog.isDef(segment), 'segment should be defined'); rTree.remove(segmentData); @@ -874,7 +923,7 @@ ol.interaction.Modify.prototype.removeVertex_ = function() { if (deleted) { this.rBush_.remove(newSegment[0]); this.rBush_.remove(newSegment[1]); - geometry.setCoordinates(coordinates); + this.setGeometryCoordinates_(geometry, coordinates); goog.asserts.assert(newIndex >= 0, 'newIndex should be larger than 0'); var newSegmentData = /** @type {ol.interaction.SegmentDataType} */ ({ depth: segmentData.depth, @@ -898,6 +947,19 @@ ol.interaction.Modify.prototype.removeVertex_ = function() { }; +/** + * @param {ol.geom.SimpleGeometry} geometry Geometry. + * @param {Array} coordinates Coordinates. + * @private + */ +ol.interaction.Modify.prototype.setGeometryCoordinates_ = + function(geometry, coordinates) { + this.changingFeature_ = true; + geometry.setCoordinates(coordinates); + this.changingFeature_ = false; +}; + + /** * @param {ol.geom.SimpleGeometry} geometry Geometry. * @param {number} index Index. diff --git a/test/spec/ol/interaction/modifyinteraction.test.js b/test/spec/ol/interaction/modifyinteraction.test.js index 418b98a3f4..7fff57b64f 100644 --- a/test/spec/ol/interaction/modifyinteraction.test.js +++ b/test/spec/ol/interaction/modifyinteraction.test.js @@ -262,10 +262,65 @@ describe('ol.interaction.Modify', function() { expect(feature.getGeometry().getCoordinates()[0]).to.have.length(5); }); }); + + describe('handle feature change', function() { + var getListeners; + + beforeEach(function() { + getListeners = function(feature, modify) { + var listeners = goog.events.getListeners( + feature, goog.events.EventType.CHANGE, false); + return goog.array.filter(listeners, function(listener) { + return listener.handler == modify; + }); + }; + }); + + it('updates the segment data', function() { + var modify = new ol.interaction.Modify({ + features: new ol.Collection(features) + }); + map.addInteraction(modify); + + var feature = features[0]; + var listeners, listener; + + listeners = getListeners(feature, modify); + expect(listeners).to.have.length(1); + + var firstSegmentData; + + firstSegmentData = modify.rBush_.forEachInExtent([0, 0, 5, 5], + function(node) { + return node; + }); + expect(firstSegmentData.segment[0]).to.eql([0, 0]); + expect(firstSegmentData.segment[1]).to.eql([10, 20]); + + var coordinates = feature.getGeometry().getCoordinates(); + var firstVertex = coordinates[0][0]; + firstVertex[0] = 1; + firstVertex[1] = 1; + feature.getGeometry().setCoordinates(coordinates); + + firstSegmentData = modify.rBush_.forEachInExtent([0, 0, 5, 5], + function(node) { + return node; + }); + expect(firstSegmentData.segment[0]).to.eql([1, 1]); + expect(firstSegmentData.segment[1]).to.eql([10, 20]); + + listeners = getListeners(feature, modify); + expect(listeners).to.have.length(1); + }); + }); + }); +goog.require('goog.array'); goog.require('goog.dispose'); goog.require('goog.events'); +goog.require('goog.events.EventType'); goog.require('goog.events.BrowserEvent'); goog.require('goog.style'); goog.require('ol.Collection');