diff --git a/examples/modify-features.html b/examples/modify-features.html new file mode 100644 index 0000000000..26a5a698e0 --- /dev/null +++ b/examples/modify-features.html @@ -0,0 +1,50 @@ + + + + + + + + + + + Modify features example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

Modify features example

+

Example of using the Modify interaction. Select a feature and drag the circle that appears when the cursor gets close to the selected geometry.

+
+

See the modify-features.js source to see how this is done.

+
+
modify, edit, vector
+
+ +
+ +
+ + + + + + diff --git a/examples/modify-features.js b/examples/modify-features.js new file mode 100644 index 0000000000..6e7cfbdb0c --- /dev/null +++ b/examples/modify-features.js @@ -0,0 +1,174 @@ +goog.require('ol.Map'); +goog.require('ol.RendererHint'); +goog.require('ol.View2D'); +goog.require('ol.interaction'); +goog.require('ol.interaction.Modify'); +//goog.require('ol.interaction.Select'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.MapQuest'); +goog.require('ol.source.Vector'); +goog.require('ol.source.GeoJSON'); +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({ + style: 'Aerial', + source: new ol.source.MapQuest({ + layer: 'sat' + }) +}); + +var image = new ol.style.Circle({ + radius: 5, + fill: null, + stroke: new ol.style.Stroke({color: 'red', width: 1}) +}); + +var styleFunction = function(feature) { + switch (feature.getGeometry().getType()) { + case 'Point': + return [new ol.style.Style({ + image: image + })]; + case 'Polygon': + return [new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: 'blue', + width: 3 + }), + fill: new ol.style.Fill({ + color: 'rgba(0, 0, 255, 0.1)' + }) + })]; + case 'MultiLineString': + return [new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: 'green', + width: 1 + }) + })]; + case 'MultiPolygon': + return [new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: 'yellow', + width: 1 + }), + fill: new ol.style.Fill({ + color: 'rgba(255, 255, 0, 0.1)' + }) + })]; + default: + return [new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: 'red', + width: 2 + }), + fill: new ol.style.Fill({ + color: 'rgba(255, 0, 0, 0.1)' + }) + })]; + } +}; + +var vectorSource = new ol.source.GeoJSON( + /** @type {olx.source.GeoJSONOptions} */ ({ + object: { + 'type': 'FeatureCollection', + 'crs': { + 'type': 'name', + 'properties': { + 'name': 'EPSG:3857' + } + }, + 'features': [ + { + 'type': 'Feature', + 'geometry': { + 'type': 'Point', + 'coordinates': [0, 0] + } + }, + { + 'type': 'Feature', + 'geometry': { + 'type': 'LineString', + 'coordinates': [[4e6, -2e6], [8e6, 2e6]] + } + }, + { + 'type': 'Feature', + 'geometry': { + 'type': 'Polygon', + 'coordinates': [[[-5e6, -1e6], [-4e6, 1e6], [-3e6, -1e6], [-5e6, -1e6]]] + } + }/*, + { + 'type': 'Feature', + 'geometry': { + 'type': 'MultiLineString', + 'coordinates': [ + [[-1e6, -7.5e5], [-1e6, 7.5e5]], + [[1e6, -7.5e5], [1e6, 7.5e5]], + [[-7.5e5, -1e6], [7.5e5, -1e6]], + [[-7.5e5, 1e6], [7.5e5, 1e6]] + ] + } + }, + { + 'type': 'Feature', + 'geometry': { + 'type': 'MultiPolygon', + 'coordinates': [ + [[[-5e6, 6e6], [-5e6, 8e6], [-3e6, 8e6], [-3e6, 6e6]]], + [[[-2e6, 6e6], [-2e6, 8e6], [0e6, 8e6], [0e6, 6e6]]], + [[[1e6, 6e6], [1e6, 8e6], [3e6, 8e6], [3e6, 6e6]]] + ] + } + }, + { + 'type': 'Feature', + 'geometry': { + 'type': 'GeometryCollection', + 'geometries': [ + { + 'type': 'LineString', + 'coordinates': [[-5e6, -5e6], [0e6, -5e6]] + }, + { + 'type': 'Point', + 'coordinates': [4e6, -5e6] + }, + { + 'type': 'Polygon', + 'coordinates': [[[1e6, -6e6], [2e6, -4e6], [3e6, -6e6]]] + } + ] + } + }*/ + ] + } + })); + + +var vectorLayer = new ol.layer.Vector({ + source: vectorSource, + styleFunction: styleFunction +}); + +//var select = new ol.interaction.Select(); + +var modify = new ol.interaction.Modify(); + +var map = new ol.Map({ + interactions: ol.interaction.defaults().extend([modify]), + layers: [raster, vectorLayer], + renderer: ol.RendererHint.CANVAS, + target: 'map', + view: new ol.View2D({ + center: [0, 0], + zoom: 2 + }) +}); diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 86ba8617c4..61db99f6d4 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -434,6 +434,14 @@ * @todo stability experimental */ + /** + * @typedef {Object} ol.interaction.ModifyOptions + * @property {undefined|Array.|function(ol.layer.Layer):boolean} layers + * Layers or filter function to restrict modification to a subset of layers. + * @property {number|undefined} pixelTolerance Pixel tolerance for considering + * the pointer close enough to a vertex for editing. Default is 20 pixels. + */ + /** * @typedef {Object} olx.interaction.TouchRotateOptions * @property {number|undefined} threshold Minimal angle in radians to start a rotation. diff --git a/src/ol/interaction/modifyinteraction.exports b/src/ol/interaction/modifyinteraction.exports new file mode 100644 index 0000000000..2b7b78bce7 --- /dev/null +++ b/src/ol/interaction/modifyinteraction.exports @@ -0,0 +1 @@ +@exportClass ol.interaction.Modify ol.interaction.ModifyOptions diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js new file mode 100644 index 0000000000..674df4f7b3 --- /dev/null +++ b/src/ol/interaction/modifyinteraction.js @@ -0,0 +1,656 @@ +goog.provide('ol.interaction.Modify'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.functions'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEventType'); +goog.require('ol.Feature'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.ViewHint'); +goog.require('ol.coordinate'); +goog.require('ol.extent'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.interaction.Drag'); +goog.require('ol.layer.Layer'); +goog.require('ol.layer.Vector'); +goog.require('ol.render.FeaturesOverlay'); +goog.require('ol.structs.RBush'); +goog.require('ol.style.Circle'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); + + +/** + * @typedef {{feature: ol.Feature, + * geometry: ol.geom.Geometry, + * index: (number|undefined), + * style: ol.style.Style, + * segment: Array.}} + */ +ol.interaction.SegmentDataType; + + + +/** + * @constructor + * @extends {ol.interaction.Drag} + * @param {ol.interaction.ModifyOptions=} opt_options Options. + */ +ol.interaction.Modify = function(opt_options) { + + goog.base(this); + + var options = goog.isDef(opt_options) ? opt_options : {}; + + var layerFilter = options.layers; + if (!goog.isDef(layerFilter)) { + layerFilter = goog.functions.TRUE; + } else if (goog.isArray(layerFilter)) { + layerFilter = function(layer) {return options.layers.indexOf(layer) > -1;}; + } + goog.asserts.assertFunction(layerFilter); + + /** + * @type {function(ol.layer.Layer):boolean} + * @private + */ + this.layerFilter_ = layerFilter; + + /** + * Editing vertex. + * @type {ol.Feature} + * @private + */ + this.vertexFeature_ = null; + + /** + * @type {boolean} + * @private + */ + this.modifiable_ = false; + + /** + * Segment RTree for each layer + * @type {Object.<*, ol.structs.RBush>} + * @private + */ + this.rBush_ = null; + + /** + * @type {number} + * @private + */ + this.pixelTolerance_ = goog.isDef(options.pixelTolerance) ? + options.pixelTolerance : 20; + + /** + * @type {Array} + * @private + */ + this.dragSegments_ = null; + + /** + * Draw overlay where are sketch features are drawn. + * @type {ol.render.FeaturesOverlay} + * @private + */ + this.overlay_ = new ol.render.FeaturesOverlay(); + this.overlay_.setStyleFunction(goog.isDef(options.styleFunction) ? + options.styleFunction : ol.interaction.Modify.defaultStyleFunction + ); + +}; +goog.inherits(ol.interaction.Modify, ol.interaction.Drag); + +/** + * @param {ol.Feature} feature Feature. + * @param {number} resolution Resolution. + * @return {Array.} Styles. + */ +ol.interaction.Modify.defaultStyleFunction = (function() { + /** @type {Object.>} */ + var styles = {}; + styles[ol.geom.GeometryType.POLYGON] = [ + new ol.style.Style({ + fill: new ol.style.Fill({ + color: [255, 255, 255, 0.5] + }) + }) + ]; + styles[ol.geom.GeometryType.MULTI_POLYGON] = + styles[ol.geom.GeometryType.POLYGON]; + + styles[ol.geom.GeometryType.LINE_STRING] = [ + new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: [255, 255, 255, 1], + width: 5 + }) + }), + new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: [0, 153, 255, 1], + width: 3 + }) + }) + ]; + styles[ol.geom.GeometryType.MULTI_LINE_STRING] = + styles[ol.geom.GeometryType.LINE_STRING]; + + styles[ol.geom.GeometryType.POINT] = [ + new ol.style.Style({ + image: new ol.style.Circle({ + radius: 7, + fill: new ol.style.Fill({ + color: [0, 153, 255, 1] + }), + stroke: new ol.style.Stroke({ + color: [255, 255, 255, 0.75], + width: 1.5 + }) + }), + zIndex: 100000 + }) + ]; + styles[ol.geom.GeometryType.MULTI_POINT] = + styles[ol.geom.GeometryType.POINT]; + + return function(feature, resolution) { + return styles[feature.getGeometry().getType()]; + }; +})(); + + +/** + * @inheritDoc + */ +ol.interaction.Modify.prototype.setMap = function(map) { + var oldMap = this.getMap(); + var layers; + if (!goog.isNull(oldMap)) { + layers = oldMap.getLayerGroup().getLayers(); + goog.asserts.assert(goog.isDef(layers)); + layers.forEach(goog.bind(this.removeLayer_, this)); + layers.unlisten(ol.CollectionEventType.ADD, this.handleLayerAdded_, false, + this); + layers.unlisten(ol.CollectionEventType.REMOVE, this.handleLayerRemoved_, + false, this); + } + + if (!goog.isNull(map)) { + if (goog.isNull(this.rBush_)) { + this.rBush_ = new ol.structs.RBush(); + } + layers = map.getLayerGroup().getLayers(); + goog.asserts.assert(goog.isDef(layers)); + layers.forEach(goog.bind(this.addLayer_, this)); + layers.listen(ol.CollectionEventType.ADD, this.handleLayerAdded_, false, + this); + layers.listen(ol.CollectionEventType.REMOVE, this.handleLayerRemoved_, + false, this); + } else { + // removing from a map, clean up + this.rBush_ = null; + } + + this.overlay_.setMap(map); + goog.base(this, 'setMap', map); +}; + + +/** + * @param {ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Modify.prototype.handleLayerAdded_ = function(evt) { + var layer = evt.getElement(); + goog.asserts.assertInstanceof(layer, ol.layer.Layer); + this.addLayer_(layer); +}; + + +/** + * Add a layer for modification. + * @param {ol.layer.Layer} layer Layer. + * @private + */ +ol.interaction.Modify.prototype.addLayer_ = function(layer) { + if (this.layerFilter_(layer) && layer instanceof ol.layer.Vector) { + this.addIndex_(layer.getSource().getAllFeatures(), + layer); + } +}; + + +/** + * @param {ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Modify.prototype.handleLayerRemoved_ = function(evt) { + var layer = evt.getElement(); + goog.asserts.assertInstanceof(layer, ol.layer.Layer); + this.removeLayer_(layer); +}; + + +/** + * Remove a layer for modification. + * @param {ol.layer.Layer} layer Layer. + * @private + */ +ol.interaction.Modify.prototype.removeLayer_ = function(layer) { + if (this.layerFilter_(layer) && layer instanceof ol.layer.Vector) { + this.removeIndex_(layer.getSource().getAllFeatures()); + } +}; + + +/** + * @param {Array.} features Array of features. + * @param {ol.layer.Vector} layer Layer the features belong to. + * @private + */ +ol.interaction.Modify.prototype.addIndex_ = function(features, layer) { + for (var i = 0, ii = features.length; i < ii; ++i) { + var feature = features[i]; + var geometry = feature.getGeometry(); + this.addSegments_(feature, geometry, layer); + } +}; + + +/** + * @param {Array.} features Array of features. + * @private + */ +ol.interaction.Modify.prototype.removeIndex_ = function(features) { + var rBush = this.rBush_; + var i, feature, nodesToRemove; + for (i = features.length - 1; i >= 0; --i) { + feature = features[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]); + } +}; + + +/** + * @param {ol.Feature} feature Feature to add segments for. + * @param {null|ol.geom.Geometry|undefined} geometry Geometry to add segments + * for. + * @param {ol.layer.Vector} layer Vector layer to add segments for. + * @private + */ +ol.interaction.Modify.prototype.addSegments_ = + function(feature, geometry, layer) { + var rBush = this.rBush_; + var segment, segmentData, coordinates; + if (geometry instanceof ol.geom.Point) { + coordinates = geometry.getCoordinates(); + segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + segment: [coordinates, coordinates], + style: layer.getStyleFunction() + }); + rBush.insert(geometry.getExtent(), segmentData); + } else if (geometry instanceof ol.geom.MultiPoint) { + var points = geometry.getCoordinates(); + for (var i = 0, ii = points.length - 1; i < ii; ++i) { + coordinates = points[i]; + segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + depth: [i], + segment: [coordinates, coordinates], + style: layer.getStyleFunction() + }); + rBush.insert(geometry.getExtent(), segmentData); + } + } else if (geometry instanceof ol.geom.LineString || + geometry instanceof ol.geom.LinearRing) { + coordinates = geometry.getCoordinates(); + for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + index: i, + style: layer.getStyleFunction(), + segment: segment + }); + rBush.insert(ol.extent.boundingExtent(segment), segmentData); + } + } else if (geometry instanceof ol.geom.MultiLineString) { + var lines = geometry.getCoordinates(); + for (var j = 0, jj = lines.length; j < jj; ++j) { + coordinates = lines[j]; + for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + depth: [j], + index: i, + style: layer.getStyleFunction(), + segment: segment + }); + rBush.insert(ol.extent.boundingExtent(segment), segmentData); + } + } + } else if (geometry instanceof ol.geom.Polygon) { + var rings = geometry.getCoordinates(); + coordinates = rings[0]; + for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + index: i, + style: layer.getStyleFunction(), + segment: segment + }); + rBush.insert(ol.extent.boundingExtent(segment), segmentData); + } + + } else if (geometry instanceof ol.geom.MultiPolygon) { + var polygons = geometry.getCoordinates(); + for (var j = 0, jj = polygons.length; j < jj; ++j) { + coordinates = polygons[j][0]; + for (var i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + depth: [j], + index: i, + style: layer.getStyleFunction(), + segment: segment + }); + rBush.insert(ol.extent.boundingExtent(segment), segmentData); + } + } + } +}; + + +/** + * @param {ol.style.Style} style Style of the layer that the feature being + * modified belongs to. + * @param {ol.Coordinate} coordinates Coordinates. + * @return {ol.Feature} Vertex feature. + * @private + */ +ol.interaction.Modify.prototype.createOrUpdateVertexFeature_ = + function(style, coordinates) { + var vertexFeature = this.vertexFeature_; + if (goog.isNull(vertexFeature)) { + vertexFeature = new ol.Feature(new ol.geom.Point(coordinates)); + this.vertexFeature_ = vertexFeature; + } else { + var geometry = vertexFeature.getGeometry(); + geometry.setCoordinates(coordinates); + } + this.updateSketchFeatures_(); + return vertexFeature; +}; + + +/** + * @inheritDoc + */ +ol.interaction.Modify.prototype.handleDragStart = function(evt) { + this.dragSegments_ = []; + var vertexFeature = this.vertexFeature_; + if (!goog.isNull(vertexFeature)) { + var insertVertices = []; + var vertex = vertexFeature.getGeometry().getCoordinates(); + var vertexExtent = ol.extent.boundingExtent([vertex]); + var segmentDataMatches = []; + this.rBush_.forEachInExtent(vertexExtent, + function(segmentData) { + segmentDataMatches.push(segmentData); + }); + var distinctFeatures = {}; + for (var i = 0, ii = segmentDataMatches.length; i < ii; ++i) { + var segmentDataMatch = segmentDataMatches[i]; + var segment = segmentDataMatch.segment; + if (!(goog.getUid(segmentDataMatch.feature) in distinctFeatures)) { + var feature = segmentDataMatch.feature; + distinctFeatures[goog.getUid(feature)] = true; + } + if (ol.coordinate.equals(segment[0], vertex)) { + this.dragSegments_.push([segmentDataMatch, 0]); + } else if (ol.coordinate.equals(segment[1], vertex)) { + this.dragSegments_.push([segmentDataMatch, 1]); + } else if ( + ol.coordinate.squaredDistanceToSegment(vertex, segment) === 0) { + insertVertices.push([segmentDataMatch, vertex]); + } + } + for (i = insertVertices.length - 1; i >= 0; --i) { + this.insertVertex_.apply(this, insertVertices[i]); + } + } + return this.modifiable_; +}; + + +/** + * @inheritDoc + */ +ol.interaction.Modify.prototype.handleDrag = function(evt) { + var vertex = evt.getCoordinate(); + for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) { + var dragSegment = this.dragSegments_[i]; + var segmentData = dragSegment[0]; + var depth = segmentData.depth; + var geometry = segmentData.geometry; + var coordinates = geometry.getCoordinates(); + var segment = segmentData.segment; + var index = dragSegment[1]; + + + if (geometry instanceof ol.geom.Point) { + coordinates = vertex; + segment[0] = segment[1] = vertex; + } else if (geometry instanceof ol.geom.MultiPoint) { + coordinates[depth[0]][segmentData.index + index] = vertex; + segment[0] = segment[1] = vertex; + } else if (geometry instanceof ol.geom.LineString) { + coordinates[segmentData.index + index] = vertex; + segment[index] = vertex; + } else if (geometry instanceof ol.geom.MultiLineString) { + coordinates[depth[0]][segmentData.index + index] = vertex; + segment[index] = vertex; + } else if (geometry instanceof ol.geom.Polygon) { + coordinates[0][segmentData.index + index] = vertex; + segment[index] = vertex; + } else if (geometry instanceof ol.geom.MultiPolygon) { + coordinates[depth[0]][0][segmentData.index + index] = vertex; + segment[index] = vertex; + } + + geometry.setCoordinates(coordinates); + var newBounds = ol.extent.boundingExtent(segment); + this.createOrUpdateVertexFeature_(segmentData.style, vertex); + this.rBush_.remove(segmentData); + this.rBush_.insert(newBounds, segmentData); + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.Modify.prototype.handleDragEnd = function(evt) { + var segmentData; + for (var i = this.dragSegments_.length - 1; i >= 0; --i) { + segmentData = this.dragSegments_[i][0]; + this.rBush_.update(ol.extent.boundingExtent(segmentData.segment), + segmentData); + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.Modify.prototype.handleMapBrowserEvent = + function(mapBrowserEvent) { + if (!mapBrowserEvent.map.getView().getHints()[ol.ViewHint.INTERACTING] && + !this.getDragging() && + mapBrowserEvent.type == ol.MapBrowserEvent.EventType.MOUSEMOVE) { + this.handleMouseMove_(mapBrowserEvent); + } + goog.base(this, 'handleMapBrowserEvent', mapBrowserEvent); + return !this.modifiable_; +}; + + +/** + * @param {ol.MapBrowserEvent} evt Event. + * @private + */ +ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) { + var map = evt.map; + var pixel = evt.getPixel(); + var pixelCoordinate = map.getCoordinateFromPixel(pixel); + var sortByDistance = function(a, b) { + return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) - + ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment); + }; + + 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]); + + this.modifiable_ = false; + var vertexFeature = this.vertexFeature_; + var rBush = this.rBush_; + var nodes = rBush.getAllInExtent(box); + //var renderIntent = ol.layer.VectorLayerRenderIntent.HIDDEN; + if (nodes.length > 0) { + nodes.sort(sortByDistance); + var node = nodes[0]; + var segment = node.segment; // the closest segment + var vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, segment)); + var vertexPixel = map.getPixelFromCoordinate(vertex); + if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <= + this.pixelTolerance_) { + var pixel1 = map.getPixelFromCoordinate(segment[0]); + var pixel2 = map.getPixelFromCoordinate(segment[1]); + var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1); + var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2); + var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + //renderIntent = ol.layer.VectorLayerRenderIntent.FUTURE; + if (dist <= 10) { + vertex = squaredDist1 > squaredDist2 ? segment[1] : segment[0]; + //renderIntent = ol.layer.VectorLayerRenderIntent.TEMPORARY; + } + vertexFeature = this.createOrUpdateVertexFeature_(node.style, + vertex); + this.modifiable_ = true; + } + } + + if (!goog.isNull(vertexFeature)) { + this.updateSketchFeatures_(); + } +}; + + +/** + * @param {ol.interaction.SegmentDataType} segmentData Segment data. + * @param {ol.Coordinate} vertex Vertex. + * @private + */ +ol.interaction.Modify.prototype.insertVertex_ = + function(segmentData, vertex) { + var segment = segmentData.segment; + var feature = segmentData.feature; + var geometry = segmentData.geometry; + var depth = segmentData.depth; + var index = segmentData.index; + var coordinates = geometry.getCoordinates(); + + if (geometry instanceof ol.geom.MultiPoint) { + coordinates[depth[0]] = coordinates; + } else if (geometry instanceof ol.geom.MultiLineString) { + coordinates[depth[0]].splice(index + 1, 0, vertex); + } else if (geometry instanceof ol.geom.Polygon) { + coordinates[0].splice(index + 1, 0, vertex); + } else if (geometry instanceof ol.geom.MultiPolygon) { + coordinates[depth[0]][0].splice(index + 1, 0, vertex); + } else { + coordinates.splice(index + 1, 0, vertex); + } + geometry.setCoordinates(coordinates); + var rTree = this.rBush_; + goog.asserts.assert(goog.isDef(segment)); + rTree.remove(segmentData); + var uid = goog.getUid(feature); + var segmentDataMatches = []; + this.rBush_.forEachInExtent(geometry.getExtent(), + function(segmentData) { + if (goog.getUid(segmentData.feature) === uid) { + segmentDataMatches.push(segmentData); + } + }); + for (var i = 0, ii = segmentDataMatches.length; i < ii; ++i) { + var segmentDataMatch = segmentDataMatches[i]; + if (segmentDataMatch.geometry === geometry && + segmentDataMatch.index > index) { + ++segmentDataMatch.index; + } + } + var newSegmentData = /** @type {ol.interaction.SegmentDataType} */ ({ + style: segmentData.style, + segment: [segment[0], vertex], + feature: feature, + geometry: geometry, + depth: depth, + index: index + }); + rTree.insert(ol.extent.boundingExtent(newSegmentData.segment), + newSegmentData); + this.dragSegments_.push([newSegmentData, 1]); + + var newSegmentData2 = /** @type {ol.interaction.SegmentDataType} */ ({ + style: segmentData.style, + segment: [vertex, segment[1]], + feature: feature, + geometry: geometry, + depth: depth, + index: index + 1 + }); + rTree.insert(ol.extent.boundingExtent(newSegmentData2.segment), + newSegmentData2); + this.dragSegments_.push([newSegmentData2, 0]); +}; + + +/** + * Redraw the skecth features. + * @private + */ +ol.interaction.Modify.prototype.updateSketchFeatures_ = function() { + this.overlay_.setFeatures(new ol.Collection([this.vertexFeature_])); +};