From 3155d681be58b75ac8ac58c476afbd4259adf459 Mon Sep 17 00:00:00 2001 From: oterral Date: Tue, 26 Nov 2013 17:17:19 +0100 Subject: [PATCH 01/18] Add modify feature interaction and its example --- examples/modify-features.html | 50 ++ examples/modify-features.js | 174 +++++ src/objectliterals.jsdoc | 8 + src/ol/interaction/modifyinteraction.exports | 1 + src/ol/interaction/modifyinteraction.js | 656 +++++++++++++++++++ 5 files changed, 889 insertions(+) create mode 100644 examples/modify-features.html create mode 100644 examples/modify-features.js create mode 100644 src/ol/interaction/modifyinteraction.exports create mode 100644 src/ol/interaction/modifyinteraction.js 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_])); +}; From a1d20182fe35f7c7054343c8d807a82aa18ee259 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Thu, 23 Jan 2014 10:36:09 +0100 Subject: [PATCH 02/18] Make modify interaction use FeaturesOverlay Instead of a whole map. --- examples/modify-features.html | 1 + examples/modify-features.js | 92 +++++++- src/objectliterals.jsdoc | 6 +- src/ol/featureoverlay.js | 8 + src/ol/interaction/modifyinteraction.exports | 2 +- src/ol/interaction/modifyinteraction.js | 209 +++++-------------- 6 files changed, 153 insertions(+), 165 deletions(-) diff --git a/examples/modify-features.html b/examples/modify-features.html index 26a5a698e0..b4ac93300a 100644 --- a/examples/modify-features.html +++ b/examples/modify-features.html @@ -43,6 +43,7 @@ + diff --git a/examples/modify-features.js b/examples/modify-features.js index 6e7cfbdb0c..8d43176613 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -1,19 +1,20 @@ goog.require('ol.Map'); goog.require('ol.RendererHint'); goog.require('ol.View2D'); +goog.require('ol.geom.GeometryType'); 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.render.FeaturesOverlay'); goog.require('ol.source.GeoJSON'); +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({ style: 'Aerial', source: new ol.source.MapQuest({ @@ -102,7 +103,8 @@ var vectorSource = new ol.source.GeoJSON( 'type': 'Feature', 'geometry': { 'type': 'Polygon', - 'coordinates': [[[-5e6, -1e6], [-4e6, 1e6], [-3e6, -1e6], [-5e6, -1e6]]] + 'coordinates': [[[-5e6, -1e6], [-4e6, 1e6], + [-3e6, -1e6], [-5e6, -1e6]]] } }/*, { @@ -158,9 +160,64 @@ var vectorLayer = new ol.layer.Vector({ styleFunction: styleFunction }); -//var select = new ol.interaction.Select(); +var overlayStyle = (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]; -var modify = new ol.interaction.Modify(); + 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()]; + }; +})(); + +var overlay = new ol.render.FeaturesOverlay({ + styleFunction: overlayStyle +}); + +var modify = new ol.interaction.Modify(overlay); var map = new ol.Map({ interactions: ol.interaction.defaults().extend([modify]), @@ -172,3 +229,26 @@ var map = new ol.Map({ zoom: 2 }) }); + +var highlight; +var displayFeatureInfo = function(pixel) { + + var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) { + return feature; + }); + + if (feature !== highlight) { + if (highlight) { + overlay.removeFeature(highlight); + } + if (feature) { + overlay.addFeature(feature); + } + highlight = feature; + } + +}; + +map.on('singleclick', function(evt) { + displayFeatureInfo(evt.pixel); +}); diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 61db99f6d4..d2244f9c4d 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -435,9 +435,9 @@ */ /** - * @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. + * @typedef {Object} olx.interaction.ModifyOptions + * @property {ol.feature.StyleFunction|undefined} styleFunction + * the styleFunction for the feature * @property {number|undefined} pixelTolerance Pixel tolerance for considering * the pointer close enough to a vertex for editing. Default is 20 pixels. */ diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index 9bc70adecd..982def1e5a 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -242,3 +242,11 @@ ol.FeatureOverlay.prototype.setStyleFunction = function(styleFunction) { this.styleFunction_ = styleFunction; this.requestRenderFrame_(); }; + + +/** + * @return {ol.feature.StyleFunction|undefined} Style function. + */ +ol.render.FeaturesOverlay.prototype.getStyleFunction = function() { + return this.styleFunction_; +}; diff --git a/src/ol/interaction/modifyinteraction.exports b/src/ol/interaction/modifyinteraction.exports index 2b7b78bce7..60378a3759 100644 --- a/src/ol/interaction/modifyinteraction.exports +++ b/src/ol/interaction/modifyinteraction.exports @@ -1 +1 @@ -@exportClass ol.interaction.Modify ol.interaction.ModifyOptions +@exportSymbol ol.interaction.Modify diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index 674df4f7b3..e0b3749ffa 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -2,7 +2,6 @@ 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'); @@ -10,6 +9,7 @@ goog.require('ol.MapBrowserEvent.EventType'); goog.require('ol.ViewHint'); goog.require('ol.coordinate'); goog.require('ol.extent'); +goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); goog.require('ol.geom.LinearRing'); goog.require('ol.geom.MultiLineString'); @@ -18,7 +18,6 @@ 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'); @@ -42,27 +41,15 @@ ol.interaction.SegmentDataType; /** * @constructor * @extends {ol.interaction.Drag} - * @param {ol.interaction.ModifyOptions=} opt_options Options. + * @param {ol.render.FeaturesOverlay} featuresOverlay FeaturesOverlay + * @param {olx.interaction.ModifyOptions=} opt_options Options. */ -ol.interaction.Modify = function(opt_options) { +ol.interaction.Modify = function(featuresOverlay, 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. @@ -102,14 +89,17 @@ ol.interaction.Modify = function(opt_options) { * @type {ol.render.FeaturesOverlay} * @private */ - this.overlay_ = new ol.render.FeaturesOverlay(); - this.overlay_.setStyleFunction(goog.isDef(options.styleFunction) ? - options.styleFunction : ol.interaction.Modify.defaultStyleFunction - ); + this.overlay_ = featuresOverlay; + + this.overlay_.getFeatures().listen(ol.CollectionEventType.ADD, + this.addFeature_, false, this); + this.overlay_.getFeatures().listen(ol.CollectionEventType.REMOVE, + this.removeFeature_, false, this); }; goog.inherits(ol.interaction.Modify, ol.interaction.Drag); + /** * @param {ol.Feature} feature Feature. * @param {number} resolution Resolution. @@ -173,29 +163,10 @@ ol.interaction.Modify.defaultStyleFunction = (function() { * @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; @@ -210,94 +181,9 @@ ol.interaction.Modify.prototype.setMap = function(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) { +ol.interaction.Modify.prototype.addFeature_ = function(evt) { + var feature = evt.element; + var geometry = feature.getGeometry(); var rBush = this.rBush_; var segment, segmentData, coordinates; if (geometry instanceof ol.geom.Point) { @@ -306,7 +192,7 @@ ol.interaction.Modify.prototype.addSegments_ = feature: feature, geometry: geometry, segment: [coordinates, coordinates], - style: layer.getStyleFunction() + style: this.overlay_.getStyleFunction() }); rBush.insert(geometry.getExtent(), segmentData); } else if (geometry instanceof ol.geom.MultiPoint) { @@ -318,7 +204,7 @@ ol.interaction.Modify.prototype.addSegments_ = geometry: geometry, depth: [i], segment: [coordinates, coordinates], - style: layer.getStyleFunction() + style: this.overlay_.getStyleFunction() }); rBush.insert(geometry.getExtent(), segmentData); } @@ -331,7 +217,7 @@ ol.interaction.Modify.prototype.addSegments_ = feature: feature, geometry: geometry, index: i, - style: layer.getStyleFunction(), + style: this.overlay_.getStyleFunction(), segment: segment }); rBush.insert(ol.extent.boundingExtent(segment), segmentData); @@ -347,7 +233,7 @@ ol.interaction.Modify.prototype.addSegments_ = geometry: geometry, depth: [j], index: i, - style: layer.getStyleFunction(), + style: this.overlay_.getStyleFunction(), segment: segment }); rBush.insert(ol.extent.boundingExtent(segment), segmentData); @@ -362,7 +248,7 @@ ol.interaction.Modify.prototype.addSegments_ = feature: feature, geometry: geometry, index: i, - style: layer.getStyleFunction(), + style: this.overlay_.getStyleFunction(), segment: segment }); rBush.insert(ol.extent.boundingExtent(segment), segmentData); @@ -372,14 +258,14 @@ ol.interaction.Modify.prototype.addSegments_ = 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) { + for (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(), + style: this.overlay_.getStyleFunction(), segment: segment }); rBush.insert(ol.extent.boundingExtent(segment), segmentData); @@ -389,6 +275,25 @@ ol.interaction.Modify.prototype.addSegments_ = }; +/** + * @param {ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Modify.prototype.removeFeature_ = function(evt) { + var feature = evt.element; + var rBush = this.rBush_; + var i, nodesToRemove = []; + rBush.forEachInExtent(feature.getGeometry().getExtent(), function(node) { + if (feature === node.feature) { + nodesToRemove.push(node); + } + }); + for (i = nodesToRemove.length - 1; i >= 0; --i) { + rBush.remove(nodesToRemove[i]); + } +}; + + /** * @param {ol.style.Style} style Style of the layer that the feature being * modified belongs to. @@ -402,11 +307,11 @@ ol.interaction.Modify.prototype.createOrUpdateVertexFeature_ = if (goog.isNull(vertexFeature)) { vertexFeature = new ol.Feature(new ol.geom.Point(coordinates)); this.vertexFeature_ = vertexFeature; + this.overlay_.addFeature(vertexFeature); } else { - var geometry = vertexFeature.getGeometry(); + var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry()); geometry.setCoordinates(coordinates); } - this.updateSketchFeatures_(); return vertexFeature; }; @@ -419,7 +324,8 @@ ol.interaction.Modify.prototype.handleDragStart = function(evt) { var vertexFeature = this.vertexFeature_; if (!goog.isNull(vertexFeature)) { var insertVertices = []; - var vertex = vertexFeature.getGeometry().getCoordinates(); + var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry()); + var vertex = geometry.getCoordinates(); var vertexExtent = ol.extent.boundingExtent([vertex]); var segmentDataMatches = []; this.rBush_.forEachInExtent(vertexExtent, @@ -455,7 +361,7 @@ ol.interaction.Modify.prototype.handleDragStart = function(evt) { * @inheritDoc */ ol.interaction.Modify.prototype.handleDrag = function(evt) { - var vertex = evt.getCoordinate(); + var vertex = evt.coordinate; for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) { var dragSegment = this.dragSegments_[i]; var segmentData = dragSegment[0]; @@ -529,7 +435,7 @@ ol.interaction.Modify.prototype.handleMapBrowserEvent = */ ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) { var map = evt.map; - var pixel = evt.getPixel(); + var pixel = evt.pixel; var pixelCoordinate = map.getCoordinateFromPixel(pixel); var sortByDistance = function(a, b) { return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) - @@ -543,7 +449,6 @@ ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) { 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; @@ -565,14 +470,14 @@ ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) { vertex = squaredDist1 > squaredDist2 ? segment[1] : segment[0]; //renderIntent = ol.layer.VectorLayerRenderIntent.TEMPORARY; } - vertexFeature = this.createOrUpdateVertexFeature_(node.style, - vertex); + this.createOrUpdateVertexFeature_(node.style, vertex); this.modifiable_ = true; + return; } } - - if (!goog.isNull(vertexFeature)) { - this.updateSketchFeatures_(); + if (!goog.isNull(this.vertexFeature_)) { + this.overlay_.removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; } }; @@ -589,6 +494,9 @@ ol.interaction.Modify.prototype.insertVertex_ = var geometry = segmentData.geometry; var depth = segmentData.depth; var index = segmentData.index; + geometry = /** @type {ol.geom.Point|ol.geom.LineString|ol.geom.Polygon| + ol.geom.MultiPoint|ol.geom.MultiLineString|ol.geom.MultiPolygon} */ + (geometry); var coordinates = geometry.getCoordinates(); if (geometry instanceof ol.geom.MultiPoint) { @@ -645,12 +553,3 @@ ol.interaction.Modify.prototype.insertVertex_ = 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_])); -}; From 20ea1b5141af7947f14d8a2ce02c53a6b0aa941c Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Mon, 3 Feb 2014 12:37:02 +0100 Subject: [PATCH 03/18] Bring MultiPolygons back in examples --- examples/modify-features.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/modify-features.js b/examples/modify-features.js index 8d43176613..56fa644bf6 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -106,7 +106,7 @@ var vectorSource = new ol.source.GeoJSON( 'coordinates': [[[-5e6, -1e6], [-4e6, 1e6], [-3e6, -1e6], [-5e6, -1e6]]] } - }/*, + }, { 'type': 'Feature', 'geometry': { @@ -124,9 +124,12 @@ var vectorSource = new ol.source.GeoJSON( '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]]] + [[[-5e6, 6e6], [-5e6, 8e6], [-3e6, 8e6], + [-3e6, 6e6], [-5e6, 6e6]]], + [[[-2e6, 6e6], [-2e6, 8e6], [0e6, 8e6], + [0e6, 6e6], [-2e6, 6e6]]], + [[[1e6, 6e6], [1e6, 8e6], [3e6, 8e6], + [3e6, 6e6], [1e6, 6e6]]] ] } }, @@ -149,7 +152,7 @@ var vectorSource = new ol.source.GeoJSON( } ] } - }*/ + } ] } })); From fb637aedca1f34c98dfe07da5cc5c83d82124d6d Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Mon, 3 Feb 2014 12:37:38 +0100 Subject: [PATCH 04/18] Move variables declaration at the top --- src/ol/interaction/modifyinteraction.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index e0b3749ffa..190d7120a0 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -186,6 +186,7 @@ ol.interaction.Modify.prototype.addFeature_ = function(evt) { var geometry = feature.getGeometry(); var rBush = this.rBush_; var segment, segmentData, coordinates; + var i, ii; if (geometry instanceof ol.geom.Point) { coordinates = geometry.getCoordinates(); segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ @@ -197,7 +198,7 @@ ol.interaction.Modify.prototype.addFeature_ = function(evt) { 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) { + for (i = 0, ii = points.length - 1; i < ii; ++i) { coordinates = points[i]; segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ feature: feature, @@ -211,7 +212,7 @@ ol.interaction.Modify.prototype.addFeature_ = function(evt) { } 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) { + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { segment = coordinates.slice(i, i + 2); segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ feature: feature, @@ -226,7 +227,7 @@ ol.interaction.Modify.prototype.addFeature_ = function(evt) { 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) { + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { segment = coordinates.slice(i, i + 2); segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ feature: feature, @@ -242,7 +243,7 @@ ol.interaction.Modify.prototype.addFeature_ = function(evt) { } 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) { + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { segment = coordinates.slice(i, i + 2); segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ feature: feature, From 933ef64db166e44cc0f2b7ca6873b0e85fb6e1da Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Mon, 3 Feb 2014 15:58:24 +0100 Subject: [PATCH 05/18] =?UTF-8?q?Force=20remove=20vertexFeature=20when=20t?= =?UTF-8?q?here=E2=80=99s=20no=20features=20left=20in=20FeaturesOverlay?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ol/interaction/modifyinteraction.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index 190d7120a0..96bff355d0 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -292,6 +292,13 @@ ol.interaction.Modify.prototype.removeFeature_ = function(evt) { for (i = nodesToRemove.length - 1; i >= 0; --i) { rBush.remove(nodesToRemove[i]); } + // There remains only vertexFeature… + if (!goog.isNull(this.vertexFeature_) && + this.overlay_.getFeatures().getLength() === 1 && + this.overlay_.getFeatures().getAt(0) == this.vertexFeature_) { + this.overlay_.removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; + } }; From 1c4926b591677998e0117a43a77f0f5da3fe7da6 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Mon, 3 Feb 2014 16:37:04 +0100 Subject: [PATCH 06/18] Ensure styles are defined in FeaturesOverlay --- src/ol/featureoverlay.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index 982def1e5a..bd776851ff 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -149,6 +149,9 @@ ol.FeatureOverlay.prototype.handleMapPostCompose_ = function(event) { var i, ii, feature, styles; this.features_.forEach(function(feature) { styles = this.styleFunction_(feature, resolution); + if (!goog.isDefAndNotNull(styles)) { + return; + } ii = styles.length; for (i = 0; i < ii; ++i) { vectorContext.drawFeature(feature, styles[i]); From ad1cec84a1132caf104a1b00861268190d1cb9e6 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Tue, 4 Feb 2014 11:34:47 +0100 Subject: [PATCH 07/18] Add style for Geomettry Collection --- examples/modify-features.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/examples/modify-features.js b/examples/modify-features.js index 56fa644bf6..16df4b37f3 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -171,10 +171,24 @@ var overlayStyle = (function() { fill: new ol.style.Fill({ color: [255, 255, 255, 0.5] }) + }), + 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_POLYGON] = styles[ol.geom.GeometryType.POLYGON]; + styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] = + styles[ol.geom.GeometryType.POLYGON]; styles[ol.geom.GeometryType.LINE_STRING] = [ new ol.style.Style({ From a60203f9318a8c54e64e0e19a69b5e5aabc51550 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Tue, 4 Feb 2014 11:35:15 +0100 Subject: [PATCH 08/18] Move segment writers in their own methods --- src/ol/interaction/modifyinteraction.js | 235 +++++++++++++++--------- 1 file changed, 150 insertions(+), 85 deletions(-) diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index 96bff355d0..e79c1d53d5 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -11,14 +11,12 @@ goog.require('ol.coordinate'); goog.require('ol.extent'); goog.require('ol.geom.GeometryType'); 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.Vector'); goog.require('ol.render.FeaturesOverlay'); goog.require('ol.structs.RBush'); goog.require('ol.style.Circle'); @@ -96,6 +94,21 @@ ol.interaction.Modify = function(featuresOverlay, opt_options) { this.overlay_.getFeatures().listen(ol.CollectionEventType.REMOVE, this.removeFeature_, false, this); + /** + * @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_ + }; + }; goog.inherits(ol.interaction.Modify, ol.interaction.Drag); @@ -183,94 +196,149 @@ ol.interaction.Modify.prototype.setMap = function(map) { */ ol.interaction.Modify.prototype.addFeature_ = function(evt) { var feature = evt.element; + goog.asserts.assertInstanceof(feature, ol.Feature); var geometry = feature.getGeometry(); - var rBush = this.rBush_; - var segment, segmentData, coordinates; - var i, ii; - if (geometry instanceof ol.geom.Point) { - coordinates = geometry.getCoordinates(); + this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Point} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writePointGeometry_ = + function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var segmentData = /** @type {ol.interaction.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + segment: [coordinates, coordinates], + style: this.overlay_.getStyleFunction() + }); + this.rBush_.insert(geometry.getExtent(), segmentData); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPoint} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writeMultiPointGeometry_ = + function(feature, geometry) { + var points = geometry.getCoordinates(); + var coordinates, i, ii, segmentData; + for (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: this.overlay_.getStyleFunction() }); - rBush.insert(geometry.getExtent(), segmentData); - } else if (geometry instanceof ol.geom.MultiPoint) { - var points = geometry.getCoordinates(); - for (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: this.overlay_.getStyleFunction() - }); - rBush.insert(geometry.getExtent(), segmentData); - } - } else if (geometry instanceof ol.geom.LineString || - geometry instanceof ol.geom.LinearRing) { - coordinates = geometry.getCoordinates(); - for (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: this.overlay_.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 (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: this.overlay_.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 (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: this.overlay_.getStyleFunction(), - segment: segment - }); - rBush.insert(ol.extent.boundingExtent(segment), segmentData); - } + this.rBush_.insert(geometry.getExtent(), 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 (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: this.overlay_.getStyleFunction(), - segment: segment - }); - rBush.insert(ol.extent.boundingExtent(segment), segmentData); - } + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.LineString} geometry Geometry. + * @private + */ +ol.interaction.Modify.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.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + index: i, + style: this.overlay_.getStyleFunction(), + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiLineString} geometry Geometry. + * @private + */ +ol.interaction.Modify.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.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + depth: [j], + index: i, + style: this.overlay_.getStyleFunction(), + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Polygon} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writePolygonGeometry_ = + function(feature, geometry) { + var rings = geometry.getCoordinates(); + var coordinates = rings[0]; + 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.SegmentDataType} */ ({ + feature: feature, + geometry: geometry, + index: i, + style: this.overlay_.getStyleFunction(), + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPolygon} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writeMultiPolygonGeometry_ = + function(feature, geometry) { + var polygons = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = polygons.length; j < jj; ++j) { + coordinates = polygons[j][0]; + for (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: this.overlay_.getStyleFunction(), + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); } } }; @@ -459,7 +527,6 @@ ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) { this.modifiable_ = false; 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]; @@ -473,10 +540,8 @@ ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) { 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; } this.createOrUpdateVertexFeature_(node.style, vertex); this.modifiable_ = true; From e64766f01f02e33068481ad4b3ad4026dddd6794 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Tue, 4 Feb 2014 14:11:30 +0100 Subject: [PATCH 09/18] Add support for GeometryCollection in modify interaction --- src/ol/interaction/modifyinteraction.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index e79c1d53d5..a60d985bb6 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -106,7 +106,8 @@ ol.interaction.Modify = function(featuresOverlay, opt_options) { 'Polygon': this.writePolygonGeometry_, 'MultiPoint': this.writeMultiPointGeometry_, 'MultiLineString': this.writeMultiLineStringGeometry_, - 'MultiPolygon': this.writeMultiPolygonGeometry_ + 'MultiPolygon': this.writeMultiPolygonGeometry_, + 'GeometryCollection': this.writeGeometryCollectionGeometry_ }; }; @@ -344,6 +345,21 @@ ol.interaction.Modify.prototype.writeMultiPolygonGeometry_ = }; +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.GeometryCollection} geometry Geometry. + * @private + */ +ol.interaction.Modify.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.CollectionEvent} evt Event. * @private From 9d591ba7ef4cd9d592d14940220ab19412ea480d Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Tue, 4 Feb 2014 14:26:22 +0100 Subject: [PATCH 10/18] Use Select interaction in modify interaction example. --- examples/modify-features.js | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/examples/modify-features.js b/examples/modify-features.js index 16df4b37f3..ef900db35e 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -4,6 +4,7 @@ goog.require('ol.View2D'); goog.require('ol.geom.GeometryType'); 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.render.FeaturesOverlay'); @@ -235,9 +236,12 @@ var overlay = new ol.render.FeaturesOverlay({ }); var modify = new ol.interaction.Modify(overlay); +var select = new ol.interaction.Select({ + featuresOverlay: overlay +}); var map = new ol.Map({ - interactions: ol.interaction.defaults().extend([modify]), + interactions: ol.interaction.defaults().extend([select, modify]), layers: [raster, vectorLayer], renderer: ol.RendererHint.CANVAS, target: 'map', @@ -246,26 +250,3 @@ var map = new ol.Map({ zoom: 2 }) }); - -var highlight; -var displayFeatureInfo = function(pixel) { - - var feature = map.forEachFeatureAtPixel(pixel, function(feature, layer) { - return feature; - }); - - if (feature !== highlight) { - if (highlight) { - overlay.removeFeature(highlight); - } - if (feature) { - overlay.addFeature(feature); - } - highlight = feature; - } - -}; - -map.on('singleclick', function(evt) { - displayFeatureInfo(evt.pixel); -}); From 5325af26d66c0231e21769806cb9fbc150e5c24c Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Thu, 6 Feb 2014 12:05:01 +0100 Subject: [PATCH 11/18] Store mouse last position to draw vertex at feature selection --- src/ol/interaction/modifyinteraction.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index a60d985bb6..462ae5ebed 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -62,6 +62,12 @@ ol.interaction.Modify = function(featuresOverlay, opt_options) { */ this.modifiable_ = false; + /** + * @type {ol.Pixel} + * @private + */ + this.lastPixel_ = null; + /** * Segment RTree for each layer * @type {Object.<*, ol.structs.RBush>} @@ -200,6 +206,7 @@ ol.interaction.Modify.prototype.addFeature_ = function(evt) { goog.asserts.assertInstanceof(feature, ol.Feature); var geometry = feature.getGeometry(); this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); + this.handleMouseAtPixel_(this.lastPixel_, this.getMap()); }; @@ -526,8 +533,17 @@ ol.interaction.Modify.prototype.handleMapBrowserEvent = * @private */ ol.interaction.Modify.prototype.handleMouseMove_ = function(evt) { - var map = evt.map; - var pixel = evt.pixel; + this.lastPixel_ = evt.pixel; + this.handleMouseAtPixel_(evt.pixel, evt.map); +}; + + +/** + * @param {ol.Pixel} pixel Pixel + * @param {ol.Map} map Map. + * @private + */ +ol.interaction.Modify.prototype.handleMouseAtPixel_ = function(pixel, map) { var pixelCoordinate = map.getCoordinateFromPixel(pixel); var sortByDistance = function(a, b) { return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) - From 72675bf6ad98617226eb68969df9573d48c349a8 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Thu, 6 Feb 2014 15:24:09 +0100 Subject: [PATCH 12/18] Make interaction ctor only have an option object --- examples/modify-features.js | 6 ++---- src/objectliterals.jsdoc | 1 + src/ol/interaction/modifyinteraction.js | 9 +++------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/modify-features.js b/examples/modify-features.js index ef900db35e..0a508d4dea 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -235,10 +235,8 @@ var overlay = new ol.render.FeaturesOverlay({ styleFunction: overlayStyle }); -var modify = new ol.interaction.Modify(overlay); -var select = new ol.interaction.Select({ - featuresOverlay: overlay -}); +var modify = new ol.interaction.Modify({ featuresOverlay: overlay }); +var select = new ol.interaction.Select({ featuresOverlay: overlay }); var map = new ol.Map({ interactions: ol.interaction.defaults().extend([select, modify]), diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index d2244f9c4d..fe3528acfc 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -440,6 +440,7 @@ * the styleFunction for the feature * @property {number|undefined} pixelTolerance Pixel tolerance for considering * the pointer close enough to a vertex for editing. Default is 20 pixels. + * @property {ol.render.FeaturesOverlay} featuresOverlay Features overlay. */ /** diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index 462ae5ebed..10eeaa3de3 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -39,15 +39,12 @@ ol.interaction.SegmentDataType; /** * @constructor * @extends {ol.interaction.Drag} - * @param {ol.render.FeaturesOverlay} featuresOverlay FeaturesOverlay - * @param {olx.interaction.ModifyOptions=} opt_options Options. + * @param {olx.interaction.ModifyOptions} options Options. */ -ol.interaction.Modify = function(featuresOverlay, opt_options) { +ol.interaction.Modify = function(options) { goog.base(this); - var options = goog.isDef(opt_options) ? opt_options : {}; - /** * Editing vertex. @@ -93,7 +90,7 @@ ol.interaction.Modify = function(featuresOverlay, opt_options) { * @type {ol.render.FeaturesOverlay} * @private */ - this.overlay_ = featuresOverlay; + this.overlay_ = options.featuresOverlay; this.overlay_.getFeatures().listen(ol.CollectionEventType.ADD, this.addFeature_, false, this); From c95ffe381bbf4c3b7a98b983fe5fe37ef1d6e4de Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Thu, 6 Feb 2014 15:27:43 +0100 Subject: [PATCH 13/18] Render style for Point inside GeometryCollection. --- examples/modify-features.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/examples/modify-features.js b/examples/modify-features.js index 0a508d4dea..21b4727c0c 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -26,7 +26,7 @@ var raster = new ol.layer.Tile({ var image = new ol.style.Circle({ radius: 5, fill: null, - stroke: new ol.style.Stroke({color: 'red', width: 1}) + stroke: new ol.style.Stroke({color: 'orange', width: 2}) }); var styleFunction = function(feature) { @@ -49,7 +49,7 @@ var styleFunction = function(feature) { return [new ol.style.Style({ stroke: new ol.style.Stroke({ color: 'green', - width: 1 + width: 3 }) })]; case 'MultiPolygon': @@ -66,11 +66,12 @@ var styleFunction = function(feature) { return [new ol.style.Style({ stroke: new ol.style.Stroke({ color: 'red', - width: 2 + width: 3 }), fill: new ol.style.Fill({ color: 'rgba(255, 0, 0, 0.1)' - }) + }), + image: image })]; } }; @@ -188,8 +189,6 @@ var overlayStyle = (function() { ]; styles[ol.geom.GeometryType.MULTI_POLYGON] = styles[ol.geom.GeometryType.POLYGON]; - styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] = - styles[ol.geom.GeometryType.POLYGON]; styles[ol.geom.GeometryType.LINE_STRING] = [ new ol.style.Style({ @@ -226,6 +225,10 @@ var overlayStyle = (function() { styles[ol.geom.GeometryType.MULTI_POINT] = styles[ol.geom.GeometryType.POINT]; + styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] = + styles[ol.geom.GeometryType.POLYGON].concat( + styles[ol.geom.GeometryType.POINT]); + return function(feature, resolution) { return styles[feature.getGeometry().getType()]; }; From 0272659118a8d8a46ad0ac30cf760e4197d12d3d Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Thu, 6 Feb 2014 15:43:27 +0100 Subject: [PATCH 14/18] Remove useless style method --- src/objectliterals.jsdoc | 2 - src/ol/interaction/modifyinteraction.js | 63 ------------------------- 2 files changed, 65 deletions(-) diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index fe3528acfc..1bd4f39684 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -436,8 +436,6 @@ /** * @typedef {Object} olx.interaction.ModifyOptions - * @property {ol.feature.StyleFunction|undefined} styleFunction - * the styleFunction for the feature * @property {number|undefined} pixelTolerance Pixel tolerance for considering * the pointer close enough to a vertex for editing. Default is 20 pixels. * @property {ol.render.FeaturesOverlay} featuresOverlay Features overlay. diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index 10eeaa3de3..f550c54e42 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -9,7 +9,6 @@ goog.require('ol.MapBrowserEvent.EventType'); goog.require('ol.ViewHint'); goog.require('ol.coordinate'); goog.require('ol.extent'); -goog.require('ol.geom.GeometryType'); goog.require('ol.geom.LineString'); goog.require('ol.geom.MultiLineString'); goog.require('ol.geom.MultiPoint'); @@ -19,9 +18,6 @@ goog.require('ol.geom.Polygon'); goog.require('ol.interaction.Drag'); 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'); @@ -117,65 +113,6 @@ ol.interaction.Modify = function(options) { 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 */ From f9b6eabfef0d683b988e5fbae52e22887fa77143 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Fri, 7 Feb 2014 15:09:36 +0100 Subject: [PATCH 15/18] %s/render.FeaturesOverlay/FeatureOverlay/ --- examples/modify-features.js | 8 ++++---- src/objectliterals.jsdoc | 2 +- src/ol/featureoverlay.js | 2 +- src/ol/interaction/modifyinteraction.js | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/modify-features.js b/examples/modify-features.js index 21b4727c0c..f29c5a82a8 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -1,3 +1,4 @@ +goog.require('ol.FeatureOverlay'); goog.require('ol.Map'); goog.require('ol.RendererHint'); goog.require('ol.View2D'); @@ -7,7 +8,6 @@ goog.require('ol.interaction.Modify'); goog.require('ol.interaction.Select'); goog.require('ol.layer.Tile'); goog.require('ol.layer.Vector'); -goog.require('ol.render.FeaturesOverlay'); goog.require('ol.source.GeoJSON'); goog.require('ol.source.MapQuest'); goog.require('ol.style.Circle'); @@ -234,12 +234,12 @@ var overlayStyle = (function() { }; })(); -var overlay = new ol.render.FeaturesOverlay({ +var overlay = new ol.FeatureOverlay({ styleFunction: overlayStyle }); -var modify = new ol.interaction.Modify({ featuresOverlay: overlay }); -var select = new ol.interaction.Select({ featuresOverlay: overlay }); +var modify = new ol.interaction.Modify({ featureOverlay: overlay }); +var select = new ol.interaction.Select({ featureOverlay: overlay }); var map = new ol.Map({ interactions: ol.interaction.defaults().extend([select, modify]), diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 1bd4f39684..fac35947f4 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -438,7 +438,7 @@ * @typedef {Object} olx.interaction.ModifyOptions * @property {number|undefined} pixelTolerance Pixel tolerance for considering * the pointer close enough to a vertex for editing. Default is 20 pixels. - * @property {ol.render.FeaturesOverlay} featuresOverlay Features overlay. + * @property {ol.FeatureOverlay} featureOverlay Features overlay. */ /** diff --git a/src/ol/featureoverlay.js b/src/ol/featureoverlay.js index bd776851ff..ccc5f68b45 100644 --- a/src/ol/featureoverlay.js +++ b/src/ol/featureoverlay.js @@ -250,6 +250,6 @@ ol.FeatureOverlay.prototype.setStyleFunction = function(styleFunction) { /** * @return {ol.feature.StyleFunction|undefined} Style function. */ -ol.render.FeaturesOverlay.prototype.getStyleFunction = function() { +ol.FeatureOverlay.prototype.getStyleFunction = function() { return this.styleFunction_; }; diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index f550c54e42..cee3d5bf42 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -5,6 +5,7 @@ goog.require('goog.asserts'); goog.require('ol.Collection'); goog.require('ol.CollectionEventType'); goog.require('ol.Feature'); +goog.require('ol.FeatureOverlay'); goog.require('ol.MapBrowserEvent.EventType'); goog.require('ol.ViewHint'); goog.require('ol.coordinate'); @@ -16,7 +17,6 @@ goog.require('ol.geom.MultiPolygon'); goog.require('ol.geom.Point'); goog.require('ol.geom.Polygon'); goog.require('ol.interaction.Drag'); -goog.require('ol.render.FeaturesOverlay'); goog.require('ol.structs.RBush'); goog.require('ol.style.Style'); @@ -83,10 +83,10 @@ ol.interaction.Modify = function(options) { /** * Draw overlay where are sketch features are drawn. - * @type {ol.render.FeaturesOverlay} + * @type {ol.FeatureOverlay} * @private */ - this.overlay_ = options.featuresOverlay; + this.overlay_ = options.featureOverlay; this.overlay_.getFeatures().listen(ol.CollectionEventType.ADD, this.addFeature_, false, this); From 919fbe335f3a92ca5a5c02ef95dc763ab6ec853e Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Fri, 7 Feb 2014 15:37:06 +0100 Subject: [PATCH 16/18] Use strings for GeometryType in examples --- examples/modify-features.js | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/examples/modify-features.js b/examples/modify-features.js index f29c5a82a8..70494a534d 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -1,8 +1,6 @@ goog.require('ol.FeatureOverlay'); goog.require('ol.Map'); -goog.require('ol.RendererHint'); goog.require('ol.View2D'); -goog.require('ol.geom.GeometryType'); goog.require('ol.interaction'); goog.require('ol.interaction.Modify'); goog.require('ol.interaction.Select'); @@ -166,9 +164,8 @@ var vectorLayer = new ol.layer.Vector({ }); var overlayStyle = (function() { - /** @type {Object.>} */ var styles = {}; - styles[ol.geom.GeometryType.POLYGON] = [ + styles['polygon'] = [ new ol.style.Style({ fill: new ol.style.Fill({ color: [255, 255, 255, 0.5] @@ -187,10 +184,9 @@ var overlayStyle = (function() { }) }) ]; - styles[ol.geom.GeometryType.MULTI_POLYGON] = - styles[ol.geom.GeometryType.POLYGON]; + styles['multipolygon'] = styles['polygon']; - styles[ol.geom.GeometryType.LINE_STRING] = [ + styles['linestring'] = [ new ol.style.Style({ stroke: new ol.style.Stroke({ color: [255, 255, 255, 1], @@ -204,10 +200,9 @@ var overlayStyle = (function() { }) }) ]; - styles[ol.geom.GeometryType.MULTI_LINE_STRING] = - styles[ol.geom.GeometryType.LINE_STRING]; + styles['multilinestring'] = styles['linestring']; - styles[ol.geom.GeometryType.POINT] = [ + styles['point'] = [ new ol.style.Style({ image: new ol.style.Circle({ radius: 7, @@ -222,12 +217,9 @@ var overlayStyle = (function() { zIndex: 100000 }) ]; - styles[ol.geom.GeometryType.MULTI_POINT] = - styles[ol.geom.GeometryType.POINT]; + styles['multipoint'] = styles['point']; - styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] = - styles[ol.geom.GeometryType.POLYGON].concat( - styles[ol.geom.GeometryType.POINT]); + styles['geometrycollection'] = styles['polygon'].concat(styles['point']); return function(feature, resolution) { return styles[feature.getGeometry().getType()]; @@ -244,7 +236,7 @@ var select = new ol.interaction.Select({ featureOverlay: overlay }); var map = new ol.Map({ interactions: ol.interaction.defaults().extend([select, modify]), layers: [raster, vectorLayer], - renderer: ol.RendererHint.CANVAS, + renderer: 'canvas', target: 'map', view: new ol.View2D({ center: [0, 0], From 25ba60db9f4ac21ebf272e6fec8389532017ddeb Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Mon, 10 Feb 2014 10:38:28 +0100 Subject: [PATCH 17/18] Use closure for styles & fix case --- examples/modify-features.js | 112 +++++++++++++++++------------------- 1 file changed, 53 insertions(+), 59 deletions(-) diff --git a/examples/modify-features.js b/examples/modify-features.js index 70494a534d..5c55e0af10 100644 --- a/examples/modify-features.js +++ b/examples/modify-features.js @@ -21,58 +21,52 @@ var raster = new ol.layer.Tile({ }) }); -var image = new ol.style.Circle({ - radius: 5, - fill: null, - stroke: new ol.style.Stroke({color: 'orange', width: 2}) -}); - -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: 3 - }) - })]; - 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: 3 - }), - fill: new ol.style.Fill({ - color: 'rgba(255, 0, 0, 0.1)' - }), - image: image - })]; - } -}; +var styleFunction = (function() { + var styles = {}; + var image = new ol.style.Circle({ + radius: 5, + fill: null, + stroke: new ol.style.Stroke({color: 'orange', width: 2}) + }); + styles['Point'] = [new ol.style.Style({image: image})]; + styles['Polygon'] = [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)' + }) + })]; + styles['MultiLinestring'] = [new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: 'green', + width: 3 + }) + })]; + styles['MultiPolygon'] = [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)' + }) + })]; + styles['default'] = [new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: 'red', + width: 3 + }), + fill: new ol.style.Fill({ + color: 'rgba(255, 0, 0, 0.1)' + }), + image: image + })]; + return function(feature, resolution) { + return styles[feature.getGeometry().getType()] || styles['default']; + }; +})(); var vectorSource = new ol.source.GeoJSON( /** @type {olx.source.GeoJSONOptions} */ ({ @@ -165,7 +159,7 @@ var vectorLayer = new ol.layer.Vector({ var overlayStyle = (function() { var styles = {}; - styles['polygon'] = [ + styles['Polygon'] = [ new ol.style.Style({ fill: new ol.style.Fill({ color: [255, 255, 255, 0.5] @@ -184,9 +178,9 @@ var overlayStyle = (function() { }) }) ]; - styles['multipolygon'] = styles['polygon']; + styles['MultiPolygon'] = styles['Polygon']; - styles['linestring'] = [ + styles['LineString'] = [ new ol.style.Style({ stroke: new ol.style.Stroke({ color: [255, 255, 255, 1], @@ -200,9 +194,9 @@ var overlayStyle = (function() { }) }) ]; - styles['multilinestring'] = styles['linestring']; + styles['MultiLineString'] = styles['LineString']; - styles['point'] = [ + styles['Point'] = [ new ol.style.Style({ image: new ol.style.Circle({ radius: 7, @@ -217,9 +211,9 @@ var overlayStyle = (function() { zIndex: 100000 }) ]; - styles['multipoint'] = styles['point']; + styles['MultiPoint'] = styles['Point']; - styles['geometrycollection'] = styles['polygon'].concat(styles['point']); + styles['GeometryCollection'] = styles['Polygon'].concat(styles['Point']); return function(feature, resolution) { return styles[feature.getGeometry().getType()]; From 03624b5f787b1235d3c8931602221110b8862680 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Tue, 11 Feb 2014 14:36:54 +0100 Subject: [PATCH 18/18] Check segment writers existence --- src/ol/interaction/modifyinteraction.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ol/interaction/modifyinteraction.js b/src/ol/interaction/modifyinteraction.js index cee3d5bf42..b58aa189ec 100644 --- a/src/ol/interaction/modifyinteraction.js +++ b/src/ol/interaction/modifyinteraction.js @@ -139,7 +139,9 @@ ol.interaction.Modify.prototype.addFeature_ = function(evt) { var feature = evt.element; goog.asserts.assertInstanceof(feature, ol.Feature); var geometry = feature.getGeometry(); - this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); + if (goog.isDef(this.SEGMENT_WRITERS_[geometry.getType()])) { + this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); + } this.handleMouseAtPixel_(this.lastPixel_, this.getMap()); };