From d92917ac89ce64fe753ee64584143ec2f54f4ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Dub=C3=A9?= Date: Wed, 25 Mar 2015 15:10:41 -0400 Subject: [PATCH 01/11] Fix Snap -> Interaction This commit changes ol.Snap -> ol.interaction.Snap, which extends ol.interaction.Pointer. The 'pointerdown', 'pointermove' and 'pointerup' map browser events are hanlded to edit the pixel and coordinate properties to make them 'snap' to the closest feature. --- examples/snap.html | 77 ++++ examples/snap.js | 162 +++++++++ externs/olx.js | 38 +- src/ol/interaction/snapinteraction.js | 490 ++++++++++++++++++++++++++ 4 files changed, 766 insertions(+), 1 deletion(-) create mode 100644 examples/snap.html create mode 100644 examples/snap.js create mode 100644 src/ol/interaction/snapinteraction.js diff --git a/examples/snap.html b/examples/snap.html new file mode 100644 index 0000000000..b30424fabf --- /dev/null +++ b/examples/snap.html @@ -0,0 +1,77 @@ + + + + + + + + + + + Snap example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

Snap interaction example

+

Example of using the snap interaction together with + draw and modify interactions. The snap interaction must be added + last, as it needs to be the first to handle the + pointermove event.

+
+
+ +
+
+ +
+
+ + +
+
+ +
+

See the snap.js source to see how this is done.

+
+
draw, edit, modify, vector, featureoverlay, snap
+
+ +
+ +
+ + + + + + + diff --git a/examples/snap.js b/examples/snap.js new file mode 100644 index 0000000000..a644f61fe3 --- /dev/null +++ b/examples/snap.js @@ -0,0 +1,162 @@ +goog.require('ol.FeatureOverlay'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.interaction'); +goog.require('ol.interaction.Draw'); +goog.require('ol.interaction.Modify'); +goog.require('ol.interaction.Select'); +goog.require('ol.interaction.Snap'); +goog.require('ol.layer.Tile'); +goog.require('ol.source.MapQuest'); +goog.require('ol.style.Circle'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); + +var raster = new ol.layer.Tile({ + source: new ol.source.MapQuest({layer: 'sat'}) +}); + +var map = new ol.Map({ + layers: [raster], + target: 'map', + view: new ol.View({ + center: [-11000000, 4600000], + zoom: 4 + }) +}); + +// The features are not added to a regular vector layer/source, +// but to a feature overlay which holds a collection of features. +// This collection is passed to the modify and also the draw +// interaction, so that both can add or modify features. +var featureOverlay = new ol.FeatureOverlay({ + style: new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255, 255, 255, 0.2)' + }), + stroke: new ol.style.Stroke({ + color: '#ffcc33', + width: 2 + }), + image: new ol.style.Circle({ + radius: 7, + fill: new ol.style.Fill({ + color: '#ffcc33' + }) + }) + }), + map: map +}); + +var Modify = { + init: function() { + this.select = new ol.interaction.Select(); + map.addInteraction(this.select); + + this.modify = new ol.interaction.Modify({ + features: this.select.getFeatures() + }); + map.addInteraction(this.modify); + + this.setEvents(); + }, + setEvents: function() { + var selectedFeatures = this.select.getFeatures(); + + this.select.on('change:active', function() { + selectedFeatures.forEach(selectedFeatures.remove, selectedFeatures); + }); + + // since snap control duplicates the geometry of features + // when creating the index and it doesn't rebuild the index + // on 'change:geometry', modified geometries should be handled manualy + selectedFeatures.on('add', function(evt) { + // removes the feature geometry from the snap index + featureOverlay.getFeatures().remove(evt.element); + }); + selectedFeatures.on('remove', function(evt) { + // adds the feature geometry to the snap index + featureOverlay.getFeatures().push(evt.element); + }); + }, + setActive: function(active) { + this.select.setActive(active); + this.modify.setActive(active); + } +}; +Modify.init(); + + +var Draw = { + init: function() { + map.addInteraction(this.Point); + this.Point.setActive(false); + map.addInteraction(this.LineString); + this.LineString.setActive(false); + map.addInteraction(this.Polygon); + this.Polygon.setActive(false); + }, + Point: new ol.interaction.Draw({ + features: featureOverlay.getFeatures(), + type: /** @type {ol.geom.GeometryType} */ ('Point') + }), + LineString: new ol.interaction.Draw({ + features: featureOverlay.getFeatures(), + type: /** @type {ol.geom.GeometryType} */ ('LineString') + }), + Polygon: new ol.interaction.Draw({ + features: featureOverlay.getFeatures(), + type: /** @type {ol.geom.GeometryType} */ ('Polygon') + }), + getActive: function() { + return this.activeType ? this[this.activeType].getActive() : false; + }, + setActive: function(active) { + var type = optionsForm.elements['draw-type'].value; + if (active) { + this.activeType && this[this.activeType].setActive(false); + this[type].setActive(true); + this.activeType = type; + } else { + this.activeType && this[this.activeType].setActive(false); + this.activeType = null; + } + } +}; +Draw.init(); + +var optionsForm = document.getElementById('options-form'); + + +/** + * Let user change the geometry type. + * @param {Event} e Change event. + */ +optionsForm.onchange = function(e) { + var type = e.target.getAttribute('name'); + var value = e.target.value; + if (type == 'draw-type') { + Draw.getActive() && Draw.setActive(true); + } else if (type == 'interaction') { + if (value == 'modify') { + Draw.setActive(false); + Modify.setActive(true); + } else if (value == 'draw') { + Draw.setActive(true); + Modify.setActive(false); + } + } +}; + +Draw.setActive(true); +Modify.setActive(false); + + +// snap duplicates the geometries so it can create a index. +// when a gemetry is modified, index needs to be rebuild for that +// geometry by removeing the old and adding the new +var snap = new ol.interaction.Snap({ + features: featureOverlay.getFeatures() +}); +map.addInteraction(snap); diff --git a/externs/olx.js b/externs/olx.js index bb863598be..9b42facd4b 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -2450,7 +2450,7 @@ olx.interaction.ModifyOptions.prototype.deleteCondition; /** * Pixel tolerance for considering the pointer close enough to a segment or - * vertex for editing. Default is `10` pixels. + * vertex for editing. * @type {number|undefined} * @api */ @@ -2671,6 +2671,42 @@ olx.interaction.SelectOptions.prototype.toggleCondition; olx.interaction.SelectOptions.prototype.multi; +/** + * Options for snap + * @typedef {{ + * features: (Array.|ol.Collection.|undefined), + * pixelTolerance: (number|undefined), + * source: (ol.source.Vector|undefined) + * }} + * @api + */ +olx.interaction.SnapOptions; + + +/** + * Snap to this features + * @type {Array.|ol.Collection.|undefined} + * @api + */ +olx.interaction.SnapOptions.prototype.features; + + +/** + * Pixel tolerance for considering the pointer close enough to a segment or + * vertex for editing. Default is `10` pixels. + * @type {number|undefined} + * @api + */ +olx.interaction.SnapOptions.prototype.pixelTolerance; + + +/** + * Snap to features from this source + * @type {ol.source.Vector|undefined} + */ +olx.interaction.SnapOptions.prototype.source; + + /** * Namespace. * @type {Object} diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js new file mode 100644 index 0000000000..42e95a7afb --- /dev/null +++ b/src/ol/interaction/snapinteraction.js @@ -0,0 +1,490 @@ +goog.provide('ol.interaction.Snap'); +goog.provide('ol.interaction.SnapProperty'); + +goog.require('goog.asserts'); +goog.require('goog.events'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEvent'); +goog.require('ol.CollectionEventType'); +goog.require('ol.Feature'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.Observable'); +goog.require('ol.coordinate'); +goog.require('ol.extent'); +goog.require('ol.interaction.Pointer'); +goog.require('ol.source.Vector'); +goog.require('ol.source.VectorEvent'); +goog.require('ol.source.VectorEventType'); +goog.require('ol.structs.RBush'); + + + +/** + * @classdesc + * Helper class for providing snap in ol.interaction.Pointer. + * + * Example: + * + * var snap = new ol.interaction.Snap({ + * source: source + * }); + * + * @constructor + * @extends {ol.interaction.Pointer} + * @param {olx.interaction.SnapOptions=} opt_options Options. + * @api + */ +ol.interaction.Snap = function(opt_options) { + + goog.base(this, { + handleDownEvent: ol.interaction.Snap.handleDownAndUpEvent, + handleEvent: ol.interaction.Snap.handleEvent, + handleUpEvent: ol.interaction.Snap.handleDownAndUpEvent + }); + + var options = goog.isDef(opt_options) ? opt_options : {}; + + /** + * @type {?ol.source.Vector} + * @private + */ + this.source_ = goog.isDef(options.source) ? options.source : null; + + /** + * @type {?ol.Collection.} + * @private + */ + this.features_ = goog.isDef(options.features) ? + goog.isArray(options.features) ? + new ol.Collection(options.features) : + options.features : + null; + + var features; + if (!goog.isNull(this.features_)) { + features = this.features_; + } else if (!goog.isNull(this.source_)) { + features = this.source_.getFeatures(); + } + goog.asserts.assert(goog.isDef(features)); + + /** + * @type {ol.Collection.} + * @private + */ + this.featuresListenerKeys_ = new ol.Collection(); + + /** + * @type {number} + * @private + */ + this.pixelTolerance_ = goog.isDef(options.pixelTolerance) ? + options.pixelTolerance : 10; + + + /** + * Segment RTree for each layer + * @type {Object.<*, ol.structs.RBush>} + * @private + */ + this.rBush_ = new ol.structs.RBush(); + + + /** + * @const + * @private + * @type {Object. } + */ + this.SEGMENT_WRITERS_ = { + 'Point': this.writePointGeometry_, + 'LineString': this.writeLineStringGeometry_, + 'LinearRing': this.writeLineStringGeometry_, + 'Polygon': this.writePolygonGeometry_, + 'MultiPoint': this.writeMultiPointGeometry_, + 'MultiLineString': this.writeMultiLineStringGeometry_, + 'MultiPolygon': this.writeMultiPolygonGeometry_, + 'GeometryCollection': this.writeGeometryCollectionGeometry_ + }; + + + features.forEach(this.addFeature, this); +}; +goog.inherits(ol.interaction.Snap, ol.interaction.Pointer); + + +/** + * @param {ol.Feature} feature Feature. + * @api + */ +ol.interaction.Snap.prototype.addFeature = function(feature) { + var geometry = feature.getGeometry(); + if (goog.isDef(this.SEGMENT_WRITERS_[geometry.getType()])) { + this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); + } +}; +goog.exportProperty( + ol.interaction.Snap.prototype, + 'addFeature', + ol.interaction.Snap.prototype.addFeature); + + +/** + * @inheritDoc + */ +ol.interaction.Snap.prototype.setMap = function(map) { + var currentMap = this.getMap(); + var keys = this.featuresListenerKeys_; + var features = this.features_; + var source = this.source_; + + if (currentMap) { + keys.forEach(ol.Observable.unByKey, this); + keys.clear(); + } + + goog.base(this, 'setMap', map); + + if (map) { + if (!goog.isNull(features)) { + keys.push(features.on(ol.CollectionEventType.ADD, + this.handleFeatureAdd_, this)); + keys.push(features.on(ol.CollectionEventType.REMOVE, + this.handleFeatureRemove_, this)); + } else if (!goog.isNull(source)) { + keys.push(source.on(ol.source.VectorEventType.ADDFEATURE, + this.handleFeatureAdd_, this)); + keys.push(source.on(ol.source.VectorEventType.REMOVEFEATURE, + this.handleFeatureRemove_, this)); + } + } +}; + + +/** + * @param {ol.MapBrowserPointerEvent} evt Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.Snap} + * @api + */ +ol.interaction.Snap.handleDownAndUpEvent = function(evt) { + return this.handleEvent_(evt); +}; + + +/** + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.Snap} + * @api + */ +ol.interaction.Snap.handleEvent = function(mapBrowserEvent) { + var pass = true; + if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) { + pass = this.handleEvent_(mapBrowserEvent); + } + return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass; +}; + + +/** + * Handle 'pointerdown', 'pointermove' and 'pointerup' events. + * @param {ol.MapBrowserEvent} evt A move event. + * @return {boolean} Pass the event to other interactions. + * @private + */ +ol.interaction.Snap.prototype.handleEvent_ = function(evt) { + var result = this.snapTo(evt.pixel, evt.coordinate, evt.map); + if (result.snapped) { + evt.coordinate = result.vertex; + evt.pixel = result.vertexPixel; + } + return true; +}; + + +/** + * @param {ol.source.VectorEvent|ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Snap.prototype.handleFeatureAdd_ = function(evt) { + var feature; + if (evt instanceof ol.source.VectorEvent) { + feature = evt.feature; + } else if (evt instanceof ol.CollectionEvent) { + feature = evt.element; + } + goog.asserts.assertInstanceof(feature, ol.Feature); + this.addFeature(feature); +}; + + +/** + * @param {ol.source.VectorEvent|ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) { + var feature; + if (evt instanceof ol.source.VectorEvent) { + feature = evt.feature; + } else if (evt instanceof ol.CollectionEvent) { + feature = evt.element; + } + goog.asserts.assertInstanceof(feature, ol.Feature); + this.removeFeature(feature, + feature.getGeometry().getExtent()); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Point} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writePointGeometry_ = + function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPoint} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiPointGeometry_ = + function(feature, geometry) { + var points = geometry.getCoordinates(); + var coordinates, i, ii, segmentData; + for (i = 0, ii = points.length; i < ii; ++i) { + coordinates = points[i]; + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.LineString} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeLineStringGeometry_ = + function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var i, ii, segment, segmentData; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiLineString} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiLineStringGeometry_ = + function(feature, geometry) { + var lines = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = lines.length; j < jj; ++j) { + coordinates = lines[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Polygon} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writePolygonGeometry_ = + function(feature, geometry) { + var rings = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = rings.length; j < jj; ++j) { + coordinates = rings[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPolygon} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiPolygonGeometry_ = + function(feature, geometry) { + var polygons = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData; + for (k = 0, kk = polygons.length; k < kk; ++k) { + rings = polygons[k]; + for (j = 0, jj = rings.length; j < jj; ++j) { + coordinates = rings[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.GeometryCollection} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ = + function(feature, geometry) { + var i, geometries = geometry.getGeometriesArray(); + for (i = 0; i < geometries.length; ++i) { + this.SEGMENT_WRITERS_[geometries[i].getType()].call( + this, feature, geometries[i]); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.Extent} extent Extent. + * @api + */ +ol.interaction.Snap.prototype.removeFeature = function(feature, extent) { + var rBush = this.rBush_; + var i, nodesToRemove = []; + rBush.forEachInExtent(extent, function(node) { + if (feature === node.feature) { + nodesToRemove.push(node); + } + }); + for (i = nodesToRemove.length - 1; i >= 0; --i) { + rBush.remove(nodesToRemove[i]); + } +}; +goog.exportProperty( + ol.interaction.Snap.prototype, + 'removeFeature', + ol.interaction.Snap.prototype.removeFeature); + + +/** + * @param {ol.Pixel} pixel Pixel + * @param {ol.Coordinate} pixelCoordinate Coordinate + * @param {ol.Map} map Map. + * @return {ol.interaction.Snap.ResultType} Snap result + */ +ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { + + var lowerLeft = map.getCoordinateFromPixel( + [pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]); + var upperRight = map.getCoordinateFromPixel( + [pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]); + var box = ol.extent.boundingExtent([lowerLeft, upperRight]); + + var segments = this.rBush_.getInExtent(box); + var snappedToVertex = false; + var snapped = false; + var vertex = null; + var vertexPixel = null; + if (segments.length > 0) { + segments.sort(goog.partial( + ol.interaction.Snap.sortByDistance, pixelCoordinate)); + var closestSegment = segments[0].segment; + vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, + closestSegment)); + vertexPixel = map.getPixelFromCoordinate(vertex); + if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <= + this.pixelTolerance_) { + snapped = true; + var pixel1 = map.getPixelFromCoordinate(closestSegment[0]); + var pixel2 = map.getPixelFromCoordinate(closestSegment[1]); + var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1); + var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2); + var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + snappedToVertex = dist <= this.pixelTolerance_; + if (snappedToVertex) { + vertex = squaredDist1 > squaredDist2 ? + closestSegment[1] : closestSegment[0]; + vertexPixel = map.getPixelFromCoordinate(vertex); + vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])]; + } + } + } + return /** @type {ol.interaction.Snap.ResultType} */ ({ + snapped: snapped, + vertex: vertex, + vertexPixel: vertexPixel + }); +}; + + +/** + * Sort segments by distance, helper function + * @param {ol.Coordinate} pixelCoordinate Coordinate to determine distance + * @param {ol.interaction.Snap.SegmentDataType} a + * @param {ol.interaction.Snap.SegmentDataType} b + * @return {number} + */ +ol.interaction.Snap.sortByDistance = function(pixelCoordinate, a, b) { + return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) - + ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment); +}; + + +/** + * @inheritDoc + */ +ol.interaction.Snap.prototype.shouldStopEvent = goog.functions.FALSE; + + +/** + * @typedef {{ + * snapped: {boolean}, + * vertex: (ol.Coordinate|null), + * vertexPixel: (ol.Pixel|null) + * }} + */ +ol.interaction.Snap.ResultType; + + +/** + * @typedef {{ + * feature: ol.Feature, + * segment: Array. + * }} + */ +ol.interaction.Snap.SegmentDataType; From d1c3dc51ad105c54c7dccd229f6bcd5e52c7c676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Dub=C3=A9?= Date: Thu, 26 Mar 2015 08:36:24 -0400 Subject: [PATCH 02/11] Fix Drag interaction, works with modify now --- src/ol/interaction/snapinteraction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index 42e95a7afb..3c3e225217 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -179,7 +179,8 @@ ol.interaction.Snap.handleDownAndUpEvent = function(evt) { */ ol.interaction.Snap.handleEvent = function(mapBrowserEvent) { var pass = true; - if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) { + if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERDRAG || + mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) { pass = this.handleEvent_(mapBrowserEvent); } return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass; From 49a3f0a0b7bfb4395e92ae6720223695f097a826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Dub=C3=A9?= Date: Thu, 26 Mar 2015 12:24:54 -0400 Subject: [PATCH 03/11] Update geometry index inside Snap interaction --- examples/snap.js | 12 ---- src/ol/interaction/snapinteraction.js | 91 ++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 15 deletions(-) diff --git a/examples/snap.js b/examples/snap.js index a644f61fe3..007c46d951 100644 --- a/examples/snap.js +++ b/examples/snap.js @@ -67,18 +67,6 @@ var Modify = { this.select.on('change:active', function() { selectedFeatures.forEach(selectedFeatures.remove, selectedFeatures); }); - - // since snap control duplicates the geometry of features - // when creating the index and it doesn't rebuild the index - // on 'change:geometry', modified geometries should be handled manualy - selectedFeatures.on('add', function(evt) { - // removes the feature geometry from the snap index - featureOverlay.getFeatures().remove(evt.element); - }); - selectedFeatures.on('remove', function(evt) { - // adds the feature geometry to the snap index - featureOverlay.getFeatures().push(evt.element); - }); }, setActive: function(active) { this.select.setActive(active); diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index 3c3e225217..d34628c3a9 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -3,6 +3,7 @@ goog.provide('ol.interaction.SnapProperty'); goog.require('goog.asserts'); goog.require('goog.events'); +goog.require('goog.events.EventType'); goog.require('ol.Collection'); goog.require('ol.CollectionEvent'); goog.require('ol.CollectionEventType'); @@ -11,6 +12,7 @@ goog.require('ol.MapBrowserEvent.EventType'); goog.require('ol.Observable'); goog.require('ol.coordinate'); goog.require('ol.extent'); +goog.require('ol.geom.Geometry'); goog.require('ol.interaction.Pointer'); goog.require('ol.source.Vector'); goog.require('ol.source.VectorEvent'); @@ -74,6 +76,12 @@ ol.interaction.Snap = function(opt_options) { */ this.featuresListenerKeys_ = new ol.Collection(); + /** + * @type {Object.} + * @private + */ + this.geometryListenerKeys_ = {}; + /** * @type {number} * @private @@ -106,21 +114,51 @@ ol.interaction.Snap = function(opt_options) { 'GeometryCollection': this.writeGeometryCollectionGeometry_ }; - features.forEach(this.addFeature, this); }; goog.inherits(ol.interaction.Snap, ol.interaction.Pointer); +/** + * Flag turned on when detecting a pointer drag|move event, and turned off when + * detecting any other type of event. Skip the update of the geometry index + * while dragging. + * @type {boolean} + * @private + */ +ol.interaction.Snap.prototype.dragging_ = false; + + +/** + * If a feature geometry changes while a pointer drag|move event occurs, the + * feature doesn't get updated right away. It will be at the next 'pointerup' + * event fired. + * @type {?ol.Feature} + * @private + */ +ol.interaction.Snap.prototype.featurePending_ = null; + + /** * @param {ol.Feature} feature Feature. + * @param {boolean=} opt_listen Whether to listen to the geometry change or not + * Defaults to `true`. * @api */ -ol.interaction.Snap.prototype.addFeature = function(feature) { +ol.interaction.Snap.prototype.addFeature = function(feature, opt_listen) { + var listen = goog.isDef(opt_listen) ? opt_listen : true; var geometry = feature.getGeometry(); if (goog.isDef(this.SEGMENT_WRITERS_[geometry.getType()])) { this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); } + + if (listen) { + var uid = goog.getUid(geometry); + this.geometryListenerKeys_[uid] = geometry.on( + goog.events.EventType.CHANGE, + goog.bind(this.handleGeometryChanged_, this, feature), + this); + } }; goog.exportProperty( ol.interaction.Snap.prototype, @@ -167,6 +205,12 @@ ol.interaction.Snap.prototype.setMap = function(map) { * @api */ ol.interaction.Snap.handleDownAndUpEvent = function(evt) { + this.dragging_ = false; + if (evt.type === ol.MapBrowserEvent.EventType.POINTERUP && + !goog.isNull(this.featurePending_)) { + this.updateFeature_(this.featurePending_); + this.featurePending_ = null; + } return this.handleEvent_(evt); }; @@ -182,6 +226,9 @@ ol.interaction.Snap.handleEvent = function(mapBrowserEvent) { if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERDRAG || mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) { pass = this.handleEvent_(mapBrowserEvent); + this.dragging_ = true; + } else { + this.dragging_ = false; } return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass; }; @@ -236,6 +283,22 @@ ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) { }; +/** + * @param {ol.Feature} feature Feature which geometry changed. + * @param {goog.events.Event} evt Event. + * @private + */ +ol.interaction.Snap.prototype.handleGeometryChanged_ = function(feature, evt) { + if (this.dragging_) { + if (goog.isNull(this.featurePending_)) { + this.featurePending_ = feature; + } + } else { + this.updateFeature_(feature); + } +}; + + /** * @param {ol.Feature} feature Feature * @param {ol.geom.Point} geometry Geometry. @@ -382,9 +445,13 @@ ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ = /** * @param {ol.Feature} feature Feature * @param {ol.Extent} extent Extent. + * @param {boolean=} opt_unlisten Whether to unlisten to the geometry change + * or not. Defaults to `true`. * @api */ -ol.interaction.Snap.prototype.removeFeature = function(feature, extent) { +ol.interaction.Snap.prototype.removeFeature = function(feature, extent, + opt_unlisten) { + var unlisten = goog.isDef(opt_unlisten) ? opt_unlisten : true; var rBush = this.rBush_; var i, nodesToRemove = []; rBush.forEachInExtent(extent, function(node) { @@ -395,6 +462,14 @@ ol.interaction.Snap.prototype.removeFeature = function(feature, extent) { for (i = nodesToRemove.length - 1; i >= 0; --i) { rBush.remove(nodesToRemove[i]); } + + if (unlisten) { + var geometry = feature.getGeometry(); + goog.asserts.assertInstanceof(geometry, ol.geom.Geometry); + var uid = goog.getUid(geometry); + ol.Observable.unByKey(this.geometryListenerKeys_[uid]); + delete this.geometryListenerKeys_[uid]; + } }; goog.exportProperty( ol.interaction.Snap.prototype, @@ -402,6 +477,16 @@ goog.exportProperty( ol.interaction.Snap.prototype.removeFeature); +/** + * @param {ol.Feature} feature Feature + * @private + */ +ol.interaction.Snap.prototype.updateFeature_ = function(feature) { + this.removeFeature(feature, feature.getGeometry().getExtent(), false); + this.addFeature(feature, false); +}; + + /** * @param {ol.Pixel} pixel Pixel * @param {ol.Coordinate} pixelCoordinate Coordinate From 5bffa99e8309d2effc0b1a417b7dbe586b4bdf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Dub=C3=A9?= Date: Thu, 26 Mar 2015 14:48:44 -0400 Subject: [PATCH 04/11] Trivial - reorder methods & comment --- examples/snap.js | 7 +- src/ol/interaction/snapinteraction.js | 485 +++++++++++++------------- 2 files changed, 249 insertions(+), 243 deletions(-) diff --git a/examples/snap.js b/examples/snap.js index 007c46d951..1629bd123d 100644 --- a/examples/snap.js +++ b/examples/snap.js @@ -140,10 +140,9 @@ optionsForm.onchange = function(e) { Draw.setActive(true); Modify.setActive(false); - -// snap duplicates the geometries so it can create a index. -// when a gemetry is modified, index needs to be rebuild for that -// geometry by removeing the old and adding the new +// The snap interaction must be added after the Modify and Draw interactions +// in order for its map browser event handlers to be fired first. Its handlers +// are responsible of doing the snapping. var snap = new ol.interaction.Snap({ features: featureOverlay.getFeatures() }); diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index d34628c3a9..5d0dc8b11e 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -23,7 +23,14 @@ goog.require('ol.structs.RBush'); /** * @classdesc - * Helper class for providing snap in ol.interaction.Pointer. + * Handles snapping of vector features while modifying or drawing them. The + * features can come from a {@link ol.source.Vector}, a {@link ol.Collection} + * or a plain array. Any interaction object that allows the user to interact + * with the features using the mouse can benefit from the snapping, as long + * as it is added before. + * + * The snap interaction modifies map browser event `coordinate` and `pixel` + * properties to force the snap to occur to any interaction that them. * * Example: * @@ -119,26 +126,6 @@ ol.interaction.Snap = function(opt_options) { goog.inherits(ol.interaction.Snap, ol.interaction.Pointer); -/** - * Flag turned on when detecting a pointer drag|move event, and turned off when - * detecting any other type of event. Skip the update of the geometry index - * while dragging. - * @type {boolean} - * @private - */ -ol.interaction.Snap.prototype.dragging_ = false; - - -/** - * If a feature geometry changes while a pointer drag|move event occurs, the - * feature doesn't get updated right away. It will be at the next 'pointerup' - * event fired. - * @type {?ol.Feature} - * @private - */ -ol.interaction.Snap.prototype.featurePending_ = null; - - /** * @param {ol.Feature} feature Feature. * @param {boolean=} opt_listen Whether to listen to the geometry change or not @@ -167,71 +154,23 @@ goog.exportProperty( /** - * @inheritDoc + * Flag turned on when detecting a pointer drag|move event, and turned off when + * detecting any other type of event. Skip the update of the geometry index + * while dragging. + * @type {boolean} + * @private */ -ol.interaction.Snap.prototype.setMap = function(map) { - var currentMap = this.getMap(); - var keys = this.featuresListenerKeys_; - var features = this.features_; - var source = this.source_; - - if (currentMap) { - keys.forEach(ol.Observable.unByKey, this); - keys.clear(); - } - - goog.base(this, 'setMap', map); - - if (map) { - if (!goog.isNull(features)) { - keys.push(features.on(ol.CollectionEventType.ADD, - this.handleFeatureAdd_, this)); - keys.push(features.on(ol.CollectionEventType.REMOVE, - this.handleFeatureRemove_, this)); - } else if (!goog.isNull(source)) { - keys.push(source.on(ol.source.VectorEventType.ADDFEATURE, - this.handleFeatureAdd_, this)); - keys.push(source.on(ol.source.VectorEventType.REMOVEFEATURE, - this.handleFeatureRemove_, this)); - } - } -}; +ol.interaction.Snap.prototype.dragging_ = false; /** - * @param {ol.MapBrowserPointerEvent} evt Event. - * @return {boolean} Stop drag sequence? - * @this {ol.interaction.Snap} - * @api + * If a feature geometry changes while a pointer drag|move event occurs, the + * feature doesn't get updated right away. It will be at the next 'pointerup' + * event fired. + * @type {?ol.Feature} + * @private */ -ol.interaction.Snap.handleDownAndUpEvent = function(evt) { - this.dragging_ = false; - if (evt.type === ol.MapBrowserEvent.EventType.POINTERUP && - !goog.isNull(this.featurePending_)) { - this.updateFeature_(this.featurePending_); - this.featurePending_ = null; - } - return this.handleEvent_(evt); -}; - - -/** - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.Snap} - * @api - */ -ol.interaction.Snap.handleEvent = function(mapBrowserEvent) { - var pass = true; - if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERDRAG || - mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) { - pass = this.handleEvent_(mapBrowserEvent); - this.dragging_ = true; - } else { - this.dragging_ = false; - } - return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass; -}; +ol.interaction.Snap.prototype.featurePending_ = null; /** @@ -299,149 +238,6 @@ ol.interaction.Snap.prototype.handleGeometryChanged_ = function(feature, evt) { }; -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.Point} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writePointGeometry_ = - function(feature, geometry) { - var coordinates = geometry.getCoordinates(); - var segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ - feature: feature, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiPoint} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeMultiPointGeometry_ = - function(feature, geometry) { - var points = geometry.getCoordinates(); - var coordinates, i, ii, segmentData; - for (i = 0, ii = points.length; i < ii; ++i) { - coordinates = points[i]; - segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ - feature: feature, - segment: [coordinates, coordinates] - }); - this.rBush_.insert(geometry.getExtent(), segmentData); - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.LineString} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeLineStringGeometry_ = - function(feature, geometry) { - var coordinates = geometry.getCoordinates(); - var i, ii, segment, segmentData; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiLineString} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeMultiLineStringGeometry_ = - function(feature, geometry) { - var lines = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, segment, segmentData; - for (j = 0, jj = lines.length; j < jj; ++j) { - coordinates = lines[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.Polygon} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writePolygonGeometry_ = - function(feature, geometry) { - var rings = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, segment, segmentData; - for (j = 0, jj = rings.length; j < jj; ++j) { - coordinates = rings[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.MultiPolygon} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeMultiPolygonGeometry_ = - function(feature, geometry) { - var polygons = geometry.getCoordinates(); - var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData; - for (k = 0, kk = polygons.length; k < kk; ++k) { - rings = polygons[k]; - for (j = 0, jj = rings.length; j < jj; ++j) { - coordinates = rings[j]; - for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { - segment = coordinates.slice(i, i + 2); - segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ - feature: feature, - segment: segment - }); - this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); - } - } - } -}; - - -/** - * @param {ol.Feature} feature Feature - * @param {ol.geom.GeometryCollection} geometry Geometry. - * @private - */ -ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ = - function(feature, geometry) { - var i, geometries = geometry.getGeometriesArray(); - for (i = 0; i < geometries.length; ++i) { - this.SEGMENT_WRITERS_[geometries[i].getType()].call( - this, feature, geometries[i]); - } -}; - - /** * @param {ol.Feature} feature Feature * @param {ol.Extent} extent Extent. @@ -478,15 +274,43 @@ goog.exportProperty( /** - * @param {ol.Feature} feature Feature - * @private + * @inheritDoc */ -ol.interaction.Snap.prototype.updateFeature_ = function(feature) { - this.removeFeature(feature, feature.getGeometry().getExtent(), false); - this.addFeature(feature, false); +ol.interaction.Snap.prototype.setMap = function(map) { + var currentMap = this.getMap(); + var keys = this.featuresListenerKeys_; + var features = this.features_; + var source = this.source_; + + if (currentMap) { + keys.forEach(ol.Observable.unByKey, this); + keys.clear(); + } + + goog.base(this, 'setMap', map); + + if (map) { + if (!goog.isNull(features)) { + keys.push(features.on(ol.CollectionEventType.ADD, + this.handleFeatureAdd_, this)); + keys.push(features.on(ol.CollectionEventType.REMOVE, + this.handleFeatureRemove_, this)); + } else if (!goog.isNull(source)) { + keys.push(source.on(ol.source.VectorEventType.ADDFEATURE, + this.handleFeatureAdd_, this)); + keys.push(source.on(ol.source.VectorEventType.REMOVEFEATURE, + this.handleFeatureRemove_, this)); + } + } }; +/** + * @inheritDoc + */ +ol.interaction.Snap.prototype.shouldStopEvent = goog.functions.FALSE; + + /** * @param {ol.Pixel} pixel Pixel * @param {ol.Coordinate} pixelCoordinate Coordinate @@ -539,22 +363,156 @@ ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { /** - * Sort segments by distance, helper function - * @param {ol.Coordinate} pixelCoordinate Coordinate to determine distance - * @param {ol.interaction.Snap.SegmentDataType} a - * @param {ol.interaction.Snap.SegmentDataType} b - * @return {number} + * @param {ol.Feature} feature Feature + * @private */ -ol.interaction.Snap.sortByDistance = function(pixelCoordinate, a, b) { - return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) - - ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment); +ol.interaction.Snap.prototype.updateFeature_ = function(feature) { + this.removeFeature(feature, feature.getGeometry().getExtent(), false); + this.addFeature(feature, false); }; /** - * @inheritDoc + * @param {ol.Feature} feature Feature + * @param {ol.geom.GeometryCollection} geometry Geometry. + * @private */ -ol.interaction.Snap.prototype.shouldStopEvent = goog.functions.FALSE; +ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ = + function(feature, geometry) { + var i, geometries = geometry.getGeometriesArray(); + for (i = 0; i < geometries.length; ++i) { + this.SEGMENT_WRITERS_[geometries[i].getType()].call( + this, feature, geometries[i]); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.LineString} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeLineStringGeometry_ = + function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var i, ii, segment, segmentData; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiLineString} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiLineStringGeometry_ = + function(feature, geometry) { + var lines = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = lines.length; j < jj; ++j) { + coordinates = lines[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPoint} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiPointGeometry_ = + function(feature, geometry) { + var points = geometry.getCoordinates(); + var coordinates, i, ii, segmentData; + for (i = 0, ii = points.length; i < ii; ++i) { + coordinates = points[i]; + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPolygon} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiPolygonGeometry_ = + function(feature, geometry) { + var polygons = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData; + for (k = 0, kk = polygons.length; k < kk; ++k) { + rings = polygons[k]; + for (j = 0, jj = rings.length; j < jj; ++j) { + coordinates = rings[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Point} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writePointGeometry_ = + function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Polygon} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writePolygonGeometry_ = + function(feature, geometry) { + var rings = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = rings.length; j < jj; ++j) { + coordinates = rings[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.Snap.SegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; /** @@ -574,3 +532,52 @@ ol.interaction.Snap.ResultType; * }} */ ol.interaction.Snap.SegmentDataType; + + +/** + * @param {ol.MapBrowserPointerEvent} evt Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.Snap} + * @api + */ +ol.interaction.Snap.handleDownAndUpEvent = function(evt) { + this.dragging_ = false; + if (evt.type === ol.MapBrowserEvent.EventType.POINTERUP && + !goog.isNull(this.featurePending_)) { + this.updateFeature_(this.featurePending_); + this.featurePending_ = null; + } + return this.handleEvent_(evt); +}; + + +/** + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.Snap} + * @api + */ +ol.interaction.Snap.handleEvent = function(mapBrowserEvent) { + var pass = true; + if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERDRAG || + mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) { + pass = this.handleEvent_(mapBrowserEvent); + this.dragging_ = true; + } else { + this.dragging_ = false; + } + return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass; +}; + + +/** + * Sort segments by distance, helper function + * @param {ol.Coordinate} pixelCoordinate Coordinate to determine distance + * @param {ol.interaction.Snap.SegmentDataType} a + * @param {ol.interaction.Snap.SegmentDataType} b + * @return {number} + */ +ol.interaction.Snap.sortByDistance = function(pixelCoordinate, a, b) { + return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) - + ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment); +}; From 1a5d7b85d276ac7090223d8de29213aea6ccd66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Dub=C3=A9?= Date: Thu, 26 Mar 2015 15:19:33 -0400 Subject: [PATCH 05/11] Add/remove features in setMap --- src/ol/interaction/snapinteraction.js | 71 ++++++++++++++++++--------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index 5d0dc8b11e..4f29b24427 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -69,14 +69,6 @@ ol.interaction.Snap = function(opt_options) { options.features : null; - var features; - if (!goog.isNull(this.features_)) { - features = this.features_; - } else if (!goog.isNull(this.source_)) { - features = this.source_.getFeatures(); - } - goog.asserts.assert(goog.isDef(features)); - /** * @type {ol.Collection.} * @private @@ -120,8 +112,6 @@ ol.interaction.Snap = function(opt_options) { 'MultiPolygon': this.writeMultiPolygonGeometry_, 'GeometryCollection': this.writeGeometryCollectionGeometry_ }; - - features.forEach(this.addFeature, this); }; goog.inherits(ol.interaction.Snap, ol.interaction.Pointer); @@ -173,6 +163,40 @@ ol.interaction.Snap.prototype.dragging_ = false; ol.interaction.Snap.prototype.featurePending_ = null; +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Snap.prototype.forEachFeatureAdd_ = function(feature) { + this.addFeature(feature); +}; + + +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Snap.prototype.forEachFeatureRemove_ = function(feature) { + this.removeFeature(feature); +}; + + +/** + * @return {ol.Collection.|Array.} + * @private + */ +ol.interaction.Snap.prototype.getFeatures_ = function() { + var features; + if (!goog.isNull(this.features_)) { + features = this.features_; + } else if (!goog.isNull(this.source_)) { + features = this.source_.getFeatures(); + } + goog.asserts.assert(goog.isDef(features)); + return features; +}; + + /** * Handle 'pointerdown', 'pointermove' and 'pointerup' events. * @param {ol.MapBrowserEvent} evt A move event. @@ -217,8 +241,7 @@ ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) { feature = evt.element; } goog.asserts.assertInstanceof(feature, ol.Feature); - this.removeFeature(feature, - feature.getGeometry().getExtent()); + this.removeFeature(feature); }; @@ -240,14 +263,13 @@ ol.interaction.Snap.prototype.handleGeometryChanged_ = function(feature, evt) { /** * @param {ol.Feature} feature Feature - * @param {ol.Extent} extent Extent. * @param {boolean=} opt_unlisten Whether to unlisten to the geometry change * or not. Defaults to `true`. * @api */ -ol.interaction.Snap.prototype.removeFeature = function(feature, extent, - opt_unlisten) { +ol.interaction.Snap.prototype.removeFeature = function(feature, opt_unlisten) { var unlisten = goog.isDef(opt_unlisten) ? opt_unlisten : true; + var extent = feature.getGeometry().getExtent(); var rBush = this.rBush_; var i, nodesToRemove = []; rBush.forEachInExtent(extent, function(node) { @@ -279,28 +301,29 @@ goog.exportProperty( ol.interaction.Snap.prototype.setMap = function(map) { var currentMap = this.getMap(); var keys = this.featuresListenerKeys_; - var features = this.features_; - var source = this.source_; + var features = this.getFeatures_(); if (currentMap) { keys.forEach(ol.Observable.unByKey, this); keys.clear(); + features.forEach(this.forEachFeatureRemove_, this); } goog.base(this, 'setMap', map); if (map) { - if (!goog.isNull(features)) { - keys.push(features.on(ol.CollectionEventType.ADD, + if (!goog.isNull(this.features_)) { + keys.push(this.features_.on(ol.CollectionEventType.ADD, this.handleFeatureAdd_, this)); - keys.push(features.on(ol.CollectionEventType.REMOVE, + keys.push(this.features_.on(ol.CollectionEventType.REMOVE, this.handleFeatureRemove_, this)); - } else if (!goog.isNull(source)) { - keys.push(source.on(ol.source.VectorEventType.ADDFEATURE, + } else if (!goog.isNull(this.source_)) { + keys.push(this.source_.on(ol.source.VectorEventType.ADDFEATURE, this.handleFeatureAdd_, this)); - keys.push(source.on(ol.source.VectorEventType.REMOVEFEATURE, + keys.push(this.source_.on(ol.source.VectorEventType.REMOVEFEATURE, this.handleFeatureRemove_, this)); } + features.forEach(this.forEachFeatureAdd_, this); } }; @@ -367,7 +390,7 @@ ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { * @private */ ol.interaction.Snap.prototype.updateFeature_ = function(feature) { - this.removeFeature(feature, feature.getGeometry().getExtent(), false); + this.removeFeature(feature, false); this.addFeature(feature, false); }; From 299647a95754117f1c343206b4595c53f76801bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Peru=C4=8Di=C4=87?= Date: Thu, 26 Mar 2015 22:36:38 +0100 Subject: [PATCH 06/11] remove unnecessary flag dragging_ --- src/ol/interaction/snapinteraction.js | 43 ++++++++------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index 4f29b24427..a55963eca0 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -8,7 +8,6 @@ goog.require('ol.Collection'); goog.require('ol.CollectionEvent'); goog.require('ol.CollectionEventType'); goog.require('ol.Feature'); -goog.require('ol.MapBrowserEvent.EventType'); goog.require('ol.Observable'); goog.require('ol.coordinate'); goog.require('ol.extent'); @@ -46,9 +45,9 @@ goog.require('ol.structs.RBush'); ol.interaction.Snap = function(opt_options) { goog.base(this, { - handleDownEvent: ol.interaction.Snap.handleDownAndUpEvent, handleEvent: ol.interaction.Snap.handleEvent, - handleUpEvent: ol.interaction.Snap.handleDownAndUpEvent + handleDownEvent: goog.functions.TRUE, + handleUpEvent: ol.interaction.Snap.handleUpEvent }); var options = goog.isDef(opt_options) ? opt_options : {}; @@ -125,8 +124,9 @@ goog.inherits(ol.interaction.Snap, ol.interaction.Pointer); ol.interaction.Snap.prototype.addFeature = function(feature, opt_listen) { var listen = goog.isDef(opt_listen) ? opt_listen : true; var geometry = feature.getGeometry(); - if (goog.isDef(this.SEGMENT_WRITERS_[geometry.getType()])) { - this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); + var segmentWriter = this.SEGMENT_WRITERS_[geometry.getType()]; + if (goog.isDef(segmentWriter)) { + segmentWriter.call(this, feature, geometry); } if (listen) { @@ -143,16 +143,6 @@ goog.exportProperty( ol.interaction.Snap.prototype.addFeature); -/** - * Flag turned on when detecting a pointer drag|move event, and turned off when - * detecting any other type of event. Skip the update of the geometry index - * while dragging. - * @type {boolean} - * @private - */ -ol.interaction.Snap.prototype.dragging_ = false; - - /** * If a feature geometry changes while a pointer drag|move event occurs, the * feature doesn't get updated right away. It will be at the next 'pointerup' @@ -198,7 +188,7 @@ ol.interaction.Snap.prototype.getFeatures_ = function() { /** - * Handle 'pointerdown', 'pointermove' and 'pointerup' events. + * Handle all pointer events events. * @param {ol.MapBrowserEvent} evt A move event. * @return {boolean} Pass the event to other interactions. * @private @@ -251,7 +241,7 @@ ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) { * @private */ ol.interaction.Snap.prototype.handleGeometryChanged_ = function(feature, evt) { - if (this.dragging_) { + if (this.handlingDownUpSequence) { if (goog.isNull(this.featurePending_)) { this.featurePending_ = feature; } @@ -563,14 +553,12 @@ ol.interaction.Snap.SegmentDataType; * @this {ol.interaction.Snap} * @api */ -ol.interaction.Snap.handleDownAndUpEvent = function(evt) { - this.dragging_ = false; - if (evt.type === ol.MapBrowserEvent.EventType.POINTERUP && - !goog.isNull(this.featurePending_)) { +ol.interaction.Snap.handleUpEvent = function(evt) { + if (!goog.isNull(this.featurePending_)) { this.updateFeature_(this.featurePending_); this.featurePending_ = null; } - return this.handleEvent_(evt); + return false; }; @@ -581,15 +569,8 @@ ol.interaction.Snap.handleDownAndUpEvent = function(evt) { * @api */ ol.interaction.Snap.handleEvent = function(mapBrowserEvent) { - var pass = true; - if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERDRAG || - mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERMOVE) { - pass = this.handleEvent_(mapBrowserEvent); - this.dragging_ = true; - } else { - this.dragging_ = false; - } - return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass; + return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && + this.handleEvent_(mapBrowserEvent); }; From ebc18b4f682e1dd1b53f2e5989dbbedd39123dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Peru=C4=8Di=C4=87?= Date: Thu, 26 Mar 2015 23:49:29 +0100 Subject: [PATCH 07/11] fix: updateing geometry index More than one features can be be changed while handlingDownUpSequence exampe: ol.interaction.Modify supports simultaneous editing of multiple features When a geometry changes old indexed segments are searched by the feature extend and removed. New ones are added. Old feature extent sholud be used when removing the old indexed segments. --- src/ol/interaction/snapinteraction.js | 44 ++++++++++++++++++--------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index a55963eca0..e43a8249d1 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -1,12 +1,15 @@ goog.provide('ol.interaction.Snap'); goog.provide('ol.interaction.SnapProperty'); +goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.events'); goog.require('goog.events.EventType'); +goog.require('goog.object'); goog.require('ol.Collection'); goog.require('ol.CollectionEvent'); goog.require('ol.CollectionEventType'); +goog.require('ol.Extent'); goog.require('ol.Feature'); goog.require('ol.Observable'); goog.require('ol.coordinate'); @@ -80,6 +83,14 @@ ol.interaction.Snap = function(opt_options) { */ this.geometryListenerKeys_ = {}; + /** + * Extents are preserved so indexed segment can be quickly removed + * when its feature geometry changes + * @type {Object.} + * @private + */ + this.indexedFeaturesExtents_ = {}; + /** * @type {number} * @private @@ -126,12 +137,14 @@ ol.interaction.Snap.prototype.addFeature = function(feature, opt_listen) { var geometry = feature.getGeometry(); var segmentWriter = this.SEGMENT_WRITERS_[geometry.getType()]; if (goog.isDef(segmentWriter)) { + var feature_uid = goog.getUid(feature); + this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent(); segmentWriter.call(this, feature, geometry); } if (listen) { - var uid = goog.getUid(geometry); - this.geometryListenerKeys_[uid] = geometry.on( + var geom_uid = goog.getUid(geometry); + this.geometryListenerKeys_[geom_uid] = geometry.on( goog.events.EventType.CHANGE, goog.bind(this.handleGeometryChanged_, this, feature), this); @@ -147,10 +160,10 @@ goog.exportProperty( * If a feature geometry changes while a pointer drag|move event occurs, the * feature doesn't get updated right away. It will be at the next 'pointerup' * event fired. - * @type {?ol.Feature} + * @type {Object.} * @private */ -ol.interaction.Snap.prototype.featurePending_ = null; +ol.interaction.Snap.prototype.pendingFeatures_ = null; /** @@ -242,8 +255,9 @@ ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) { */ ol.interaction.Snap.prototype.handleGeometryChanged_ = function(feature, evt) { if (this.handlingDownUpSequence) { - if (goog.isNull(this.featurePending_)) { - this.featurePending_ = feature; + var uid = goog.getUid(feature); + if (!(uid in this.pendingFeatures_)) { + this.pendingFeatures_[uid] = feature; } } else { this.updateFeature_(feature); @@ -259,7 +273,9 @@ ol.interaction.Snap.prototype.handleGeometryChanged_ = function(feature, evt) { */ ol.interaction.Snap.prototype.removeFeature = function(feature, opt_unlisten) { var unlisten = goog.isDef(opt_unlisten) ? opt_unlisten : true; - var extent = feature.getGeometry().getExtent(); + var feature_uid = goog.getUid(feature); + var extent = this.indexedFeaturesExtents_[feature_uid]; + goog.asserts.assertArray(extent); var rBush = this.rBush_; var i, nodesToRemove = []; rBush.forEachInExtent(extent, function(node) { @@ -274,9 +290,9 @@ ol.interaction.Snap.prototype.removeFeature = function(feature, opt_unlisten) { if (unlisten) { var geometry = feature.getGeometry(); goog.asserts.assertInstanceof(geometry, ol.geom.Geometry); - var uid = goog.getUid(geometry); - ol.Observable.unByKey(this.geometryListenerKeys_[uid]); - delete this.geometryListenerKeys_[uid]; + var geom_uid = goog.getUid(geometry); + ol.Observable.unByKey(this.geometryListenerKeys_[geom_uid]); + delete this.geometryListenerKeys_[geom_uid]; } }; goog.exportProperty( @@ -380,6 +396,7 @@ ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { * @private */ ol.interaction.Snap.prototype.updateFeature_ = function(feature) { + console.log(3); this.removeFeature(feature, false); this.addFeature(feature, false); }; @@ -554,10 +571,9 @@ ol.interaction.Snap.SegmentDataType; * @api */ ol.interaction.Snap.handleUpEvent = function(evt) { - if (!goog.isNull(this.featurePending_)) { - this.updateFeature_(this.featurePending_); - this.featurePending_ = null; - } + goog.array.forEach(goog.object.getValues(this.pendingFeatures_), + this.updateFeature_, this); + this.pendingFeatures_ = {}; return false; }; From 42d2adb1cf58122513f55c3c02f7082d7acf0e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Peru=C4=8Di=C4=87?= Date: Fri, 27 Mar 2015 12:48:52 +0100 Subject: [PATCH 08/11] fix: repeating sort function + js annotation Sort function is now bind to this, and it uses a pixelCoordinate_ member. ol.interaction.Snap.handleEvent is removed js annotations are modified --- externs/olx.js | 5 +-- src/ol/interaction/snapinteraction.js | 45 ++++++++++++++------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/externs/olx.js b/externs/olx.js index 9b42facd4b..f7096817a3 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -2684,7 +2684,7 @@ olx.interaction.SnapOptions; /** - * Snap to this features + * Snap to these features. Either this option or source should be provided. * @type {Array.|ol.Collection.|undefined} * @api */ @@ -2701,8 +2701,9 @@ olx.interaction.SnapOptions.prototype.pixelTolerance; /** - * Snap to features from this source + * Snap to features from this source. Either this option or features should be provided * @type {ol.source.Vector|undefined} + * @api */ olx.interaction.SnapOptions.prototype.source; diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index e43a8249d1..5e9a7e400b 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -48,7 +48,7 @@ goog.require('ol.structs.RBush'); ol.interaction.Snap = function(opt_options) { goog.base(this, { - handleEvent: ol.interaction.Snap.handleEvent, + handleEvent: this.handleEvent_, handleDownEvent: goog.functions.TRUE, handleUpEvent: ol.interaction.Snap.handleUpEvent }); @@ -91,6 +91,13 @@ ol.interaction.Snap = function(opt_options) { */ this.indexedFeaturesExtents_ = {}; + /** + * Used for distance sorting in sortByDistance_ + * @type {ol.Coordinate} + * @private + */ + this.pixelCoordinate_ = null; + /** * @type {number} * @private @@ -98,6 +105,12 @@ ol.interaction.Snap = function(opt_options) { this.pixelTolerance_ = goog.isDef(options.pixelTolerance) ? options.pixelTolerance : 10; + /** + * @type {Function} + * @private + */ + this.sortByDistance_ = goog.bind(ol.interaction.Snap.sortByDistance, this); + /** * Segment RTree for each layer @@ -212,7 +225,7 @@ ol.interaction.Snap.prototype.handleEvent_ = function(evt) { evt.coordinate = result.vertex; evt.pixel = result.vertexPixel; } - return true; + return ol.interaction.Pointer.handleEvent.call(this, evt); }; @@ -360,8 +373,8 @@ ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { var vertex = null; var vertexPixel = null; if (segments.length > 0) { - segments.sort(goog.partial( - ol.interaction.Snap.sortByDistance, pixelCoordinate)); + this.pixelCoordinate_ = pixelCoordinate; + segments.sort(this.sortByDistance_); var closestSegment = segments[0].segment; vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, closestSegment)); @@ -396,7 +409,6 @@ ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { * @private */ ol.interaction.Snap.prototype.updateFeature_ = function(feature) { - console.log(3); this.removeFeature(feature, false); this.addFeature(feature, false); }; @@ -568,7 +580,6 @@ ol.interaction.Snap.SegmentDataType; * @param {ol.MapBrowserPointerEvent} evt Event. * @return {boolean} Stop drag sequence? * @this {ol.interaction.Snap} - * @api */ ol.interaction.Snap.handleUpEvent = function(evt) { goog.array.forEach(goog.object.getValues(this.pendingFeatures_), @@ -578,26 +589,16 @@ ol.interaction.Snap.handleUpEvent = function(evt) { }; -/** - * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. - * @return {boolean} `false` to stop event propagation. - * @this {ol.interaction.Snap} - * @api - */ -ol.interaction.Snap.handleEvent = function(mapBrowserEvent) { - return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && - this.handleEvent_(mapBrowserEvent); -}; - - /** * Sort segments by distance, helper function - * @param {ol.Coordinate} pixelCoordinate Coordinate to determine distance * @param {ol.interaction.Snap.SegmentDataType} a * @param {ol.interaction.Snap.SegmentDataType} b * @return {number} + * @this {ol.interaction.Snap} */ -ol.interaction.Snap.sortByDistance = function(pixelCoordinate, a, b) { - return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) - - ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment); +ol.interaction.Snap.sortByDistance = function(a, b) { + return ol.coordinate.squaredDistanceToSegment( + this.pixelCoordinate_, a.segment) - + ol.coordinate.squaredDistanceToSegment( + this.pixelCoordinate_, b.segment); }; From b34d7ce6cc2a303359ae70a88aaad1b5cf0c6929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Dub=C3=A9?= Date: Fri, 27 Mar 2015 08:58:21 -0400 Subject: [PATCH 09/11] Fix - support ol.Collection only as features --- externs/olx.js | 4 ++-- src/ol/interaction/snapinteraction.js | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/externs/olx.js b/externs/olx.js index f7096817a3..e24cc965f7 100644 --- a/externs/olx.js +++ b/externs/olx.js @@ -2674,7 +2674,7 @@ olx.interaction.SelectOptions.prototype.multi; /** * Options for snap * @typedef {{ - * features: (Array.|ol.Collection.|undefined), + * features: (ol.Collection.|undefined), * pixelTolerance: (number|undefined), * source: (ol.source.Vector|undefined) * }} @@ -2685,7 +2685,7 @@ olx.interaction.SnapOptions; /** * Snap to these features. Either this option or source should be provided. - * @type {Array.|ol.Collection.|undefined} + * @type {ol.Collection.|undefined} * @api */ olx.interaction.SnapOptions.prototype.features; diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index 5e9a7e400b..3f0ac3a1b9 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -65,11 +65,7 @@ ol.interaction.Snap = function(opt_options) { * @type {?ol.Collection.} * @private */ - this.features_ = goog.isDef(options.features) ? - goog.isArray(options.features) ? - new ol.Collection(options.features) : - options.features : - null; + this.features_ = goog.isDef(options.features) ? options.features : null; /** * @type {ol.Collection.} From 056c3ad23e8b9f6dcd8c31e1a2feb3fffd042fc1 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Fri, 27 Mar 2015 13:44:04 +0100 Subject: [PATCH 10/11] Add consistency for pointer event handlers Now all are private static functions. --- src/ol/interaction/snapinteraction.js | 40 ++++++++++++++------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index 3f0ac3a1b9..18bf8a9d22 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -48,9 +48,9 @@ goog.require('ol.structs.RBush'); ol.interaction.Snap = function(opt_options) { goog.base(this, { - handleEvent: this.handleEvent_, + handleEvent: ol.interaction.Snap.handleEvent_, handleDownEvent: goog.functions.TRUE, - handleUpEvent: ol.interaction.Snap.handleUpEvent + handleUpEvent: ol.interaction.Snap.handleUpEvent_ }); var options = goog.isDef(opt_options) ? opt_options : {}; @@ -209,22 +209,6 @@ ol.interaction.Snap.prototype.getFeatures_ = function() { }; -/** - * Handle all pointer events events. - * @param {ol.MapBrowserEvent} evt A move event. - * @return {boolean} Pass the event to other interactions. - * @private - */ -ol.interaction.Snap.prototype.handleEvent_ = function(evt) { - var result = this.snapTo(evt.pixel, evt.coordinate, evt.map); - if (result.snapped) { - evt.coordinate = result.vertex; - evt.pixel = result.vertexPixel; - } - return ol.interaction.Pointer.handleEvent.call(this, evt); -}; - - /** * @param {ol.source.VectorEvent|ol.CollectionEvent} evt Event. * @private @@ -572,12 +556,30 @@ ol.interaction.Snap.ResultType; ol.interaction.Snap.SegmentDataType; +/** + * Handle all pointer events events. + * @param {ol.MapBrowserEvent} evt A move event. + * @return {boolean} Pass the event to other interactions. + * @this {ol.interaction.Snap} + * @private + */ +ol.interaction.Snap.handleEvent_ = function(evt) { + var result = this.snapTo(evt.pixel, evt.coordinate, evt.map); + if (result.snapped) { + evt.coordinate = result.vertex; + evt.pixel = result.vertexPixel; + } + return ol.interaction.Pointer.handleEvent.call(this, evt); +}; + + /** * @param {ol.MapBrowserPointerEvent} evt Event. * @return {boolean} Stop drag sequence? * @this {ol.interaction.Snap} + * @private */ -ol.interaction.Snap.handleUpEvent = function(evt) { +ol.interaction.Snap.handleUpEvent_ = function(evt) { goog.array.forEach(goog.object.getValues(this.pendingFeatures_), this.updateFeature_, this); this.pendingFeatures_ = {}; From d740640321d98dd6585ebf2a25876c6a0b3e68d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20Dub=C3=A9?= Date: Fri, 27 Mar 2015 09:07:34 -0400 Subject: [PATCH 11/11] Fix type of source and features --- src/ol/interaction/snapinteraction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/interaction/snapinteraction.js b/src/ol/interaction/snapinteraction.js index 18bf8a9d22..30f7014c2a 100644 --- a/src/ol/interaction/snapinteraction.js +++ b/src/ol/interaction/snapinteraction.js @@ -56,13 +56,13 @@ ol.interaction.Snap = function(opt_options) { var options = goog.isDef(opt_options) ? opt_options : {}; /** - * @type {?ol.source.Vector} + * @type {ol.source.Vector} * @private */ this.source_ = goog.isDef(options.source) ? options.source : null; /** - * @type {?ol.Collection.} + * @type {ol.Collection.} * @private */ this.features_ = goog.isDef(options.features) ? options.features : null;