From a1d20182fe35f7c7054343c8d807a82aa18ee259 Mon Sep 17 00:00:00 2001 From: Antoine Abt Date: Thu, 23 Jan 2014 10:36:09 +0100 Subject: [PATCH] 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_])); -};