From 2b71154bb2c1fcda0812ca4dc0de9b89a40998e7 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 28 Jan 2014 19:37:31 +0100 Subject: [PATCH 1/3] Add ol.interaction.Select --- src/objectliterals.jsdoc | 19 +++ src/ol/interaction/selectinteraction.exports | 3 + src/ol/interaction/selectinteraction.js | 141 +++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 src/ol/interaction/selectinteraction.exports create mode 100644 src/ol/interaction/selectinteraction.js diff --git a/src/objectliterals.jsdoc b/src/objectliterals.jsdoc index 811a0bf6ce..1dc393a348 100644 --- a/src/objectliterals.jsdoc +++ b/src/objectliterals.jsdoc @@ -394,6 +394,25 @@ * @todo stability experimental */ +/** + * @typedef {Object} olx.interaction.SelectOptions + * @property {ol.events.ConditionType|undefined} addCondition A conditional + * modifier (e.g. shift key) that determines if the selection is added to + * the current selection. By default, a shift-click adds to the current + * selection. + * @property {ol.events.ConditionType|undefined} condition A conditional + * modifier (e.g. shift key) that determines if the interaction is active + * (i.e. selection occurs) or not. By default, a click with no modifier keys + * toggles the selection. + * @property {function(ol.layer.Layer): boolean|undefined} layerFilter Filter + * function to restrict selection to a subset of layers. + * @property {ol.layer.Layer|undefined} layer Layer. The single layer from which + * features should be selected. + * @property {Array.|undefined} layers Layers. Zero or more + * layers from which features should be selected. + * @property {ol.render.FeaturesOverlay} featuresOverlay Features overlay. + */ + /** * @typedef {Object} olx.interaction.TouchPanOptions * @property {ol.Kinetic|undefined} kinetic Kinetic inertia to apply to the diff --git a/src/ol/interaction/selectinteraction.exports b/src/ol/interaction/selectinteraction.exports new file mode 100644 index 0000000000..2f926953a1 --- /dev/null +++ b/src/ol/interaction/selectinteraction.exports @@ -0,0 +1,3 @@ +@exportSymbol ol.interaction.Select +@exportProperty ol.interaction.Select.prototype.getFeaturesOverlay +@exportProperty ol.interaction.Select.prototype.setMap diff --git a/src/ol/interaction/selectinteraction.js b/src/ol/interaction/selectinteraction.js new file mode 100644 index 0000000000..8eb16eef06 --- /dev/null +++ b/src/ol/interaction/selectinteraction.js @@ -0,0 +1,141 @@ +goog.provide('ol.interaction.Select'); + +goog.require('goog.array'); +goog.require('goog.functions'); +goog.require('ol.events.condition'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.render.FeaturesOverlay'); + + + +/** + * @constructor + * @extends {ol.interaction.Interaction} + * @param {olx.interaction.SelectOptions=} opt_options Options. + */ +ol.interaction.Select = function(opt_options) { + + var options = goog.isDef(opt_options) ? opt_options : {}; + + goog.base(this); + + /** + * @private + * @type {ol.events.ConditionType} + */ + this.condition_ = goog.isDef(options.condition) ? + options.condition : ol.events.condition.singleClick; + + /** + * @private + * @type {ol.events.ConditionType} + */ + this.addCondition_ = goog.isDef(options.addCondition) ? + options.addCondition : ol.events.condition.shiftKeyOnly; + + var layerFilter; + if (goog.isDef(options.layerFilter)) { + layerFilter = options.layerFilter; + } else if (goog.isDef(options.layer)) { + var layer = options.layer; + layerFilter = function(l) { + return l === layer; + }; + } else if (goog.isDef(options.layers)) { + var layers = options.layers; + layerFilter = function(layer) { + return goog.array.indexOf(layers, layer) != -1; + }; + } else { + layerFilter = goog.functions.TRUE; + } + + /** + * @private + * @type {function(ol.layer.Layer): boolean} + */ + this.layerFilter_ = layerFilter; + + /** + * @private + * @type {ol.render.FeaturesOverlay} + */ + this.featuresOverlay_ = options.featuresOverlay; + +}; +goog.inherits(ol.interaction.Select, ol.interaction.Interaction); + + +/** + * @return {ol.render.FeaturesOverlay} Features overlay. + */ +ol.interaction.Select.prototype.getFeaturesOverlay = function() { + return this.featuresOverlay_; +}; + + +/** + * @inheritDoc + */ +ol.interaction.Select.prototype.handleMapBrowserEvent = + function(mapBrowserEvent) { + if (!this.condition_(mapBrowserEvent)) { + return true; + } + var add = this.addCondition_(mapBrowserEvent); + var map = mapBrowserEvent.map; + var features = this.featuresOverlay_.getFeatures(); + map.withFrozenRendering( + /** + * @this {ol.interaction.Select} + */ + function() { + if (add) { + map.forEachFeatureAtPixel(mapBrowserEvent.getPixel(), + /** + * @param {ol.Feature} feature Feature. + * @param {ol.layer.Layer} layer Layer. + */ + function(feature, layer) { + if (goog.array.indexOf(features.getArray(), feature) == -1) { + features.push(feature); + } + }, undefined, this.layerFilter_); + } else { + var feature = map.forEachFeatureAtPixel(mapBrowserEvent.getPixel(), + /** + * @param {ol.Feature} feature Feature. + * @param {ol.layer.Layer} layer Layer. + */ + function(feature, layer) { + return feature; + }, undefined, this.layerFilter_); + if (goog.isDef(feature)) { + if (features.getLength() == 1) { + if (features.getAt(0) !== feature) { + features.setAt(0, feature); + } + } else { + if (features.getLength() != 1) { + features.clear(); + } + features.push(feature); + } + } else { + if (features.getLength() !== 0) { + features.clear(); + } + } + } + }, this); + return false; +}; + + +/** + * @inheritDoc + */ +ol.interaction.Select.prototype.setMap = function(map) { + goog.base(this, 'setMap', map); + this.featuresOverlay_.setMap(map); +}; From d36fcc1f691ae3459a3490c6398c355205c2be85 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 28 Jan 2014 19:37:44 +0100 Subject: [PATCH 2/3] Add select-features example --- examples/select-features.html | 50 +++++++++++++++++++++++++++++ examples/select-features.js | 60 +++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 examples/select-features.html create mode 100644 examples/select-features.js diff --git a/examples/select-features.html b/examples/select-features.html new file mode 100644 index 0000000000..a764084d44 --- /dev/null +++ b/examples/select-features.html @@ -0,0 +1,50 @@ + + + + + + + + + + + Select features example + + + + + +
+ +
+
+
+
+
+ +
+ +
+

Select features example

+

Example of using the Select interaction. Select features by clicking polygons. Hold the Shift-key to add to the selection.

+
+

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

+
+
select, vector
+
+ +
+ +
+ + + + + + diff --git a/examples/select-features.js b/examples/select-features.js new file mode 100644 index 0000000000..388a411351 --- /dev/null +++ b/examples/select-features.js @@ -0,0 +1,60 @@ +goog.require('ol.Map'); +goog.require('ol.RendererHint'); +goog.require('ol.View2D'); +goog.require('ol.interaction'); +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.Fill'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); + +var raster = new ol.layer.Tile({ + source: new ol.source.MapQuest({layer: 'sat'}) +}); + +var unselectedStyle = [new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255,255,255,0.25)' + }), + stroke: new ol.style.Stroke({ + color: '#6666ff' + }) +})]; + +var selectedStyle = [new ol.style.Style({ + fill: new ol.style.Fill({ + color: 'rgba(255,255,255,0.5)' + }) +})]; + +var vector = new ol.layer.Vector({ + source: new ol.source.GeoJSON({ + url: 'data/geojson/countries.geojson' + }), + styleFunction: function(feature, layer) { + return unselectedStyle; + } +}); + +var select = new ol.interaction.Select({ + featuresOverlay: new ol.render.FeaturesOverlay({ + styleFunction: function(feature, layer) { + return selectedStyle; + } + }) +}); + +var map = new ol.Map({ + interactions: ol.interaction.defaults().extend([select]), + layers: [raster, vector], + renderer: ol.RendererHint.CANVAS, + target: 'map', + view: new ol.View2D({ + center: [0, 0], + zoom: 2 + }) +}); From 28d2d96fc782d2d746cba630acf8e756560a8936 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 28 Jan 2014 19:46:36 +0100 Subject: [PATCH 3/3] Remove old ol.interaction.Select --- old/examples/select-features.html | 50 -------- old/examples/select-features.js | 61 --------- .../ol/interaction/selectinteraction.exports | 1 - old/src/ol/interaction/selectinteraction.js | 118 ------------------ .../ol/interaction/selectinteraction.test.js | 77 ------------ 5 files changed, 307 deletions(-) delete mode 100644 old/examples/select-features.html delete mode 100644 old/examples/select-features.js delete mode 100644 old/src/ol/interaction/selectinteraction.exports delete mode 100644 old/src/ol/interaction/selectinteraction.js delete mode 100644 old/test/spec/ol/interaction/selectinteraction.test.js diff --git a/old/examples/select-features.html b/old/examples/select-features.html deleted file mode 100644 index a764084d44..0000000000 --- a/old/examples/select-features.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - Select features example - - - - - -
- -
-
-
-
-
- -
- -
-

Select features example

-

Example of using the Select interaction. Select features by clicking polygons. Hold the Shift-key to add to the selection.

-
-

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

-
-
select, vector
-
- -
- -
- - - - - - diff --git a/old/examples/select-features.js b/old/examples/select-features.js deleted file mode 100644 index 55a2e2bd85..0000000000 --- a/old/examples/select-features.js +++ /dev/null @@ -1,61 +0,0 @@ -goog.require('ol.Map'); -goog.require('ol.RendererHint'); -goog.require('ol.View2D'); -goog.require('ol.interaction'); -goog.require('ol.interaction.Select'); -goog.require('ol.layer.Tile'); -goog.require('ol.layer.Vector'); -goog.require('ol.parser.ogc.GML_v3'); -goog.require('ol.source.MapQuest'); -goog.require('ol.source.Vector'); -goog.require('ol.style.Fill'); -goog.require('ol.style.Rule'); -goog.require('ol.style.Stroke'); -goog.require('ol.style.Style'); - -var raster = new ol.layer.Tile({ - source: new ol.source.MapQuest({layer: 'sat'}) -}); - -var vector = new ol.layer.Vector({ - id: 'vector', - source: new ol.source.Vector({ - parser: new ol.parser.ogc.GML_v3(), - url: 'data/gml/topp-states-wfs.xml' - }), - style: new ol.style.Style({ - rules: [ - new ol.style.Rule({ - filter: 'renderIntent("selected")', - symbolizers: [ - new ol.style.Fill({ - color: '#ffffff', - opacity: 0.5 - }) - ] - }) - ], - symbolizers: [ - new ol.style.Fill({ - color: '#ffffff', - opacity: 0.25 - }), - new ol.style.Stroke({ - color: '#6666ff' - }) - ] - }) -}); - -var select = new ol.interaction.Select(); - -var map = new ol.Map({ - interactions: ol.interaction.defaults().extend([select]), - layers: [raster, vector], - renderer: ol.RendererHint.CANVAS, - target: 'map', - view: new ol.View2D({ - center: [-11000000, 4600000], - zoom: 4 - }) -}); diff --git a/old/src/ol/interaction/selectinteraction.exports b/old/src/ol/interaction/selectinteraction.exports deleted file mode 100644 index 7a428aa5c9..0000000000 --- a/old/src/ol/interaction/selectinteraction.exports +++ /dev/null @@ -1 +0,0 @@ -@exportSymbol ol.interaction.Select diff --git a/old/src/ol/interaction/selectinteraction.js b/old/src/ol/interaction/selectinteraction.js deleted file mode 100644 index 96cec1ad9d..0000000000 --- a/old/src/ol/interaction/selectinteraction.js +++ /dev/null @@ -1,118 +0,0 @@ -goog.provide('ol.interaction.Select'); - -goog.require('goog.array'); -goog.require('goog.asserts'); -goog.require('ol.Feature'); -goog.require('ol.FeatureRenderIntent'); -goog.require('ol.events.ConditionType'); -goog.require('ol.events.condition'); -goog.require('ol.interaction.Interaction'); -goog.require('ol.layer.Vector'); - - - -/** - * Allows the user to select features on the map. - * @constructor - * @extends {ol.interaction.Interaction} - * @param {olx.interaction.SelectOptions=} opt_options Options. - * @todo stability experimental - */ -ol.interaction.Select = function(opt_options) { - var options = goog.isDef(opt_options) ? opt_options : {}; - - /** - * @private - * @type {ol.events.ConditionType} - */ - this.condition_ = goog.isDef(options.condition) ? - options.condition : ol.events.condition.singleClick; - - /** - * @private - * @type {ol.events.ConditionType} - */ - this.addCondition_ = goog.isDef(options.addCondition) ? - options.addCondition : ol.events.condition.shiftKeyOnly; - - 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; - - goog.base(this); -}; -goog.inherits(ol.interaction.Select, ol.interaction.Interaction); - - -/** - * @inheritDoc. - */ -ol.interaction.Select.prototype.handleMapBrowserEvent = - function(mapBrowserEvent) { - if (this.condition_(mapBrowserEvent)) { - var map = mapBrowserEvent.map; - var layers = goog.array.filter( - map.getLayerGroup().getLayersArray(), this.layerFilter_); - var clear = !this.addCondition_(mapBrowserEvent); - - var that = this; - var select = function(featuresByLayer) { - that.select(map, featuresByLayer, layers, clear); - }; - - map.getFeatures({ - layers: layers, - pixel: mapBrowserEvent.getPixel(), - success: select - }); - } - // TODO: Implement box selection - return true; -}; - - -/** - * @param {ol.Map} map The map where the selction event originated. - * @param {Array.>} featuresByLayer Features by layer. - * @param {Array.} layers The queried layers. - * @param {boolean} clear Whether the current layer content should be cleared. - */ -ol.interaction.Select.prototype.select = - function(map, featuresByLayer, layers, clear) { - for (var i = 0, ii = featuresByLayer.length; i < ii; ++i) { - var layer = layers[i]; - if (!(layer instanceof ol.layer.Vector)) { - // TODO Support non-vector layers and remove this - continue; - } - - var featuresToSelect = featuresByLayer[i]; - var selectedFeatures = layer.getVectorSource().getFeatures( - ol.layer.Vector.selectedFeaturesFilter); - if (clear) { - for (var j = selectedFeatures.length - 1; j >= 0; --j) { - selectedFeatures[j].setRenderIntent( - ol.FeatureRenderIntent.DEFAULT); - } - } - for (var j = featuresToSelect.length - 1; j >= 0; --j) { - var feature = featuresToSelect[j]; - // TODO: Make toggle configurable - feature.setRenderIntent(feature.getRenderIntent() == - ol.FeatureRenderIntent.SELECTED ? - ol.FeatureRenderIntent.DEFAULT : - ol.FeatureRenderIntent.SELECTED); - } - // TODO: Dispatch an event with selectedFeatures and unselectedFeatures - } -}; diff --git a/old/test/spec/ol/interaction/selectinteraction.test.js b/old/test/spec/ol/interaction/selectinteraction.test.js deleted file mode 100644 index 473653d352..0000000000 --- a/old/test/spec/ol/interaction/selectinteraction.test.js +++ /dev/null @@ -1,77 +0,0 @@ -goog.provide('ol.test.interaction.Select'); - -describe('ol.interaction.Select', function() { - var map, target, select, source, vector, features; - - beforeEach(function() { - target = document.createElement('div'); - target.style.width = '256px'; - target.style.height = '256px'; - document.body.appendChild(target); - map = new ol.Map({ - target: target - }); - - features = [ - new ol.Feature({ - geometry: new ol.geom.Point([-1, 1]) - }), - new ol.Feature({ - geometry: new ol.geom.Point([1, -1]) - }) - ]; - - source = new ol.source.Vector({}); - source.addFeatures(features); - vector = new ol.layer.Vector({source: source}); - select = new ol.interaction.Select({ - layers: [vector] - }); - map.getInteractions().push(select); - }); - - afterEach(function() { - goog.dispose(select); - goog.dispose(map); - document.body.removeChild(target); - select = null; - map = null; - target = null; - }); - - describe('#select', function() { - - var selectedFeaturesFilter = function(feature) { - return feature.getRenderIntent() == 'selected'; - }; - - it('toggles selection of features', function() { - select.select(map, [features], [vector]); - expect(source.getFeatures(selectedFeaturesFilter).length).to.be(2); - select.select(map, [features], [vector]); - expect(source.getFeatures(selectedFeaturesFilter).length).to.be(0); - }); - - it('can append features to an existing selection', function() { - select.select(map, [[features[0]]], [vector], true); - select.select(map, [[features[1]]], [vector]); - expect(source.getFeatures(selectedFeaturesFilter).length).to.be(2); - }); - - it('can clear a selection before selecting new features', function() { - select.select(map, [[features[0]]], [vector], true); - select.select(map, [[features[1]]], [vector], true); - expect(source.getFeatures(selectedFeaturesFilter).length).to.be(1); - }); - - }); - -}); - -goog.require('goog.dispose'); -goog.require('ol.Feature'); -goog.require('ol.Map'); -goog.require('ol.geom.Point'); -goog.require('ol.interaction.Select'); -goog.require('ol.layer.Vector'); -goog.require('ol.source.Vector');