diff --git a/src/ol/expr/expression.js b/src/ol/expr/expression.js
index 25eb4163e5..ce22d86f67 100644
--- a/src/ol/expr/expression.js
+++ b/src/ol/expr/expression.js
@@ -277,7 +277,7 @@ ol.expr.lib[ol.expr.functions.GEOMETRY_TYPE] = function(type) {
* @this {ol.Feature}
*/
ol.expr.lib[ol.expr.functions.RENDER_INTENT] = function(renderIntent) {
- return this.renderIntent == renderIntent;
+ return this.getRenderIntent() == renderIntent;
};
diff --git a/src/ol/feature.js b/src/ol/feature.js
index 4adffbe482..caebeb56c5 100644
--- a/src/ol/feature.js
+++ b/src/ol/feature.js
@@ -45,8 +45,9 @@ ol.Feature = function(opt_values) {
/**
* The render intent for this feature.
* @type {ol.layer.VectorLayerRenderIntent|string}
+ * @private
*/
- this.renderIntent = ol.layer.VectorLayerRenderIntent.DEFAULT;
+ this.renderIntent_ = ol.layer.VectorLayerRenderIntent.DEFAULT;
/**
* @type {Array.
}
@@ -175,6 +176,29 @@ ol.Feature.prototype.setGeometry = function(geometry) {
};
+/**
+ * Gets the renderIntent for this feature.
+ * @return {string} Render intent.
+ */
+ol.Feature.prototype.getRenderIntent = function() {
+ return this.renderIntent_;
+};
+
+
+/**
+ * Changes the renderIntent for this feature.
+ * @param {string} renderIntent Render intent.
+ */
+ol.Feature.prototype.setRenderIntent = function(renderIntent) {
+ this.renderIntent_ = renderIntent;
+ var geometry = this.getGeometry();
+ if (!goog.isNull(geometry)) {
+ this.dispatchEvent(new ol.FeatureEvent(
+ ol.FeatureEventType.INTENTCHANGE, this, geometry.getBounds()));
+ }
+};
+
+
/**
* Set the symbolizers to be used for this feature.
* @param {Array.} symbolizers Symbolizers for this
@@ -196,7 +220,8 @@ ol.Feature.DEFAULT_GEOMETRY = 'geometry';
* @enum {string}
*/
ol.FeatureEventType = {
- CHANGE: 'featurechange'
+ CHANGE: 'featurechange',
+ INTENTCHANGE: 'featureintentchange'
};
diff --git a/src/ol/interaction/selectinteraction.js b/src/ol/interaction/selectinteraction.js
index 705e872671..eafea029ed 100644
--- a/src/ol/interaction/selectinteraction.js
+++ b/src/ol/interaction/selectinteraction.js
@@ -1,14 +1,12 @@
goog.provide('ol.interaction.Select');
goog.require('goog.array');
-goog.require('goog.object');
goog.require('ol.Feature');
goog.require('ol.events.ConditionType');
goog.require('ol.events.condition');
goog.require('ol.interaction.Interaction');
goog.require('ol.layer.Vector');
goog.require('ol.layer.VectorLayerRenderIntent');
-goog.require('ol.source.Vector');
@@ -36,20 +34,6 @@ ol.interaction.Select = function(opt_options) {
this.addCondition_ = goog.isDef(options.addCondition) ?
options.addCondition : ol.events.condition.shiftKeyOnly;
- /**
- * Mapping between original features and cloned features on selection layers.
- * @type {Object.<*,Object.<*,ol.Feature>>}
- * @private
- */
- this.featureMap_ = {};
-
- /**
- * Mapping between original layers and selection layers, by map.
- * @type {Object.<*,{map:ol.Map,layers:Object.<*,ol.layer.Vector>}>}
- * @protected
- */
- this.selectionLayers = {};
-
/**
* @type {null|function(ol.layer.Layer):boolean}
* @private
@@ -99,82 +83,29 @@ ol.interaction.Select.prototype.handleMapBrowserEvent =
*/
ol.interaction.Select.prototype.select =
function(map, featuresByLayer, layers, clear) {
- var mapId = goog.getUid(map);
- if (!(mapId in this.selectionLayers)) {
- this.selectionLayers[mapId] = {map: map, layers: {}};
- }
for (var i = 0, ii = featuresByLayer.length; i < ii; ++i) {
var layer = layers[i];
- var layerId = goog.getUid(layer);
- var selectionLayer = this.selectionLayers[mapId].layers[layerId];
- if (!goog.isDef(selectionLayer)) {
- selectionLayer = new ol.layer.Vector({
- source: new ol.source.Vector({parser: null}),
- style: layer instanceof ol.layer.Vector ? layer.getStyle() : null
- });
- selectionLayer.setTemporary(true);
- map.addLayer(selectionLayer);
- this.selectionLayers[mapId].layers[layerId] = selectionLayer;
- this.featureMap_[layerId] = {};
+ if (!(layer instanceof ol.layer.Vector)) {
+ // TODO Support non-vector layers and remove this
+ continue;
}
- var selectedFeatures, unselectedFeatures;
- if (goog.isFunction(layer.setRenderIntent)) {
- selectedFeatures = [];
- unselectedFeatures = [];
- }
- var features = featuresByLayer[i];
- var numFeatures = features.length;
- var featuresToAdd = [];
- var featuresToRemove = [];
- var featureMap = this.featureMap_[layerId];
- var oldFeatureMap = featureMap;
+ var featuresToSelect = featuresByLayer[i];
+ var selectedFeatures = layer.getFeatures(
+ ol.layer.Vector.selectedFeaturesFilter);
if (clear) {
- for (var f in featureMap) {
- if (goog.isDef(unselectedFeatures)) {
- unselectedFeatures.push(layer.getFeatureWithUid(f));
- }
- featuresToRemove.push(featureMap[f]);
- }
- featureMap = {};
- this.featureMap_[layerId] = featureMap;
- }
- for (var j = 0; j < numFeatures; ++j) {
- var feature = features[j];
- var featureId = goog.getUid(feature);
- var clone = featureMap[featureId];
- if (clone) {
- // TODO: make toggle configurable
- if (goog.isDef(unselectedFeatures)) {
- unselectedFeatures.push(feature);
- }
- delete featureMap[featureId];
- featuresToRemove.push(clone);
- } else if (!(featureId in oldFeatureMap)) {
- clone = new ol.Feature(feature.getAttributes());
- clone.setGeometry(feature.getGeometry().clone());
- clone.setId(feature.getId());
- clone.setSymbolizers(feature.getSymbolizers());
- clone.renderIntent = ol.layer.VectorLayerRenderIntent.SELECTED;
- featureMap[featureId] = clone;
- if (goog.isDef(selectedFeatures)) {
- selectedFeatures.push(feature);
- }
- featuresToAdd.push(clone);
+ for (var j = selectedFeatures.length - 1; j >= 0; --j) {
+ selectedFeatures[j].setRenderIntent(
+ ol.layer.VectorLayerRenderIntent.DEFAULT);
}
}
- if (goog.isFunction(layer.setRenderIntent)) {
- layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.HIDDEN,
- selectedFeatures);
- layer.setRenderIntent(ol.layer.VectorLayerRenderIntent.DEFAULT,
- unselectedFeatures);
- }
- selectionLayer.removeFeatures(featuresToRemove);
- selectionLayer.addFeatures(featuresToAdd);
- if (goog.object.getCount(featureMap) == 0) {
- map.removeLayer(selectionLayer);
- delete this.selectionLayers[mapId].layers[layerId];
- delete this.featureMap_[layerId];
+ for (var j = featuresToSelect.length - 1; j >= 0; --j) {
+ var feature = featuresToSelect[j];
+ // TODO: Make toggle configurable
+ feature.setRenderIntent(feature.getRenderIntent() ==
+ ol.layer.VectorLayerRenderIntent.SELECTED ?
+ ol.layer.VectorLayerRenderIntent.DEFAULT :
+ ol.layer.VectorLayerRenderIntent.SELECTED);
}
// TODO: Dispatch an event with selectedFeatures and unselectedFeatures
}
diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js
index 235433a756..71da158900 100644
--- a/src/ol/layer/vectorlayer.js
+++ b/src/ol/layer/vectorlayer.js
@@ -10,6 +10,7 @@ goog.require('ol.Feature');
goog.require('ol.FeatureEventType');
goog.require('ol.extent');
goog.require('ol.layer.Layer');
+goog.require('ol.layer.VectorLayerRenderIntent');
goog.require('ol.proj');
goog.require('ol.source.Vector');
goog.require('ol.structs.RTree');
@@ -103,16 +104,6 @@ ol.layer.FeatureCache.prototype.getFeaturesByIds_ = function(ids) {
};
-/**
- * @param {string} uid Feature uid.
- * @return {ol.Feature|undefined} The feature with the provided uid if it is in
- * the cache, otherwise undefined.
- */
-ol.layer.FeatureCache.prototype.getFeatureWithUid = function(uid) {
- return this.idLookup_[uid];
-};
-
-
/**
* Remove a feature from the cache.
* @param {ol.Feature} feature Feature.
@@ -188,6 +179,8 @@ ol.layer.Vector.prototype.addFeatures = function(features) {
}
goog.events.listen(feature, ol.FeatureEventType.CHANGE,
this.handleFeatureChange_, false, this);
+ goog.events.listen(feature, ol.FeatureEventType.INTENTCHANGE,
+ this.handleIntentChange_, false, this);
}
this.dispatchEvent(new ol.layer.VectorEvent(ol.layer.VectorEventType.ADD,
features, [extent]));
@@ -217,6 +210,23 @@ ol.layer.Vector.prototype.handleFeatureChange_ = function(evt) {
};
+/**
+ * Listener for render intent change events of features.
+ * @param {ol.FeatureEvent} evt The feature intent change event.
+ * @private
+ */
+ol.layer.Vector.prototype.handleIntentChange_ = function(evt) {
+ goog.asserts.assertInstanceof(evt.target, ol.Feature);
+ var feature = /** @type {ol.Feature} */ (evt.target);
+ var geometry = feature.getGeometry();
+ if (!goog.isNull(geometry)) {
+ this.dispatchEvent(new ol.layer.VectorEvent(
+ ol.layer.VectorEventType.INTENTCHANGE, [feature],
+ [geometry.getBounds()]));
+ }
+};
+
+
/**
* Remove all features from the layer.
*/
@@ -251,6 +261,30 @@ ol.layer.Vector.prototype.getStyle = function() {
};
+/**
+ * Returns an array of features that match a filter. This will not fetch data,
+ * it only considers features that are loaded already.
+ * @param {(function(ol.Feature):boolean)=} opt_filter Filter function.
+ * @return {Array.} Features that match the filter, or all features
+ * if no filter was provided.
+ */
+ol.layer.Vector.prototype.getFeatures = function(opt_filter) {
+ var result;
+ var features = this.featureCache_.getFeaturesObject();
+ if (goog.isDef(opt_filter)) {
+ result = [];
+ for (var f in features) {
+ if (opt_filter(features[f]) === true) {
+ result.push(features[f]);
+ }
+ }
+ } else {
+ result = goog.object.getValues(features);
+ }
+ return result;
+};
+
+
/**
* Get all features whose bounding box intersects the provided extent. This
* method is intended for being called by the renderer. When null is returned,
@@ -332,16 +366,6 @@ ol.layer.Vector.prototype.groupFeaturesBySymbolizerLiteral =
};
-/**
- * @param {string|number} uid Feature uid.
- * @return {ol.Feature|undefined} The feature with the provided uid if it is on
- * the layer, otherwise undefined.
- */
-ol.layer.Vector.prototype.getFeatureWithUid = function(uid) {
- return this.featureCache_.getFeatureWithUid(/** @type {string} */ (uid));
-};
-
-
/**
* @param {Object|Element|Document|string} data Feature data.
* @param {ol.parser.Parser} parser Feature parser.
@@ -423,31 +447,6 @@ ol.layer.Vector.prototype.removeFeatures = function(features) {
};
-/**
- * Changes the renderIntent for an array of features.
- * @param {string} renderIntent Render intent.
- * @param {Array.=} opt_features Features to change the renderIntent
- * for. If not provided, all features will be changed.
- */
-ol.layer.Vector.prototype.setRenderIntent =
- function(renderIntent, opt_features) {
- var features = goog.isDef(opt_features) ? opt_features :
- goog.object.getValues(this.featureCache_.getFeaturesObject());
- var extent = ol.extent.createEmpty(),
- feature, geometry;
- for (var i = features.length - 1; i >= 0; --i) {
- feature = features[i];
- feature.renderIntent = renderIntent;
- geometry = feature.getGeometry();
- if (!goog.isNull(geometry)) {
- ol.extent.extend(extent, geometry.getBounds());
- }
- }
- this.dispatchEvent(new ol.layer.VectorEvent(
- ol.layer.VectorEventType.INTENTCHANGE, features, [extent]));
-};
-
-
/**
* @param {boolean} temp Whether this layer is temporary.
*/
@@ -479,6 +478,15 @@ ol.layer.Vector.uidTransformFeatureInfo = function(features) {
};
+/**
+ * @param {ol.Feature} feature Feature.
+ * @return {boolean} Whether the feature is selected.
+ */
+ol.layer.Vector.selectedFeaturesFilter = function(feature) {
+ return feature.getRenderIntent() == ol.layer.VectorLayerRenderIntent.SELECTED;
+};
+
+
/**
* @constructor
@@ -511,6 +519,6 @@ goog.inherits(ol.layer.VectorEvent, goog.events.Event);
ol.layer.VectorEventType = {
ADD: 'featureadd',
CHANGE: 'featurechange',
- REMOVE: 'featureremove',
- INTENTCHANGE: 'intentchange'
+ INTENTCHANGE: 'featureintentchange',
+ REMOVE: 'featureremove'
};
diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
index 2cc87bc590..5e39824950 100644
--- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
+++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
@@ -275,7 +275,8 @@ ol.renderer.canvas.VectorLayer.prototype.getFeaturesForPixel =
halfWidth, halfHeight, uid, coordinates, j;
for (var id in candidates) {
candidate = candidates[id];
- if (candidate.renderIntent == ol.layer.VectorLayerRenderIntent.HIDDEN) {
+ if (candidate.getRenderIntent() ==
+ ol.layer.VectorLayerRenderIntent.HIDDEN) {
continue;
}
geom = candidate.getGeometry();
diff --git a/src/ol/renderer/canvas/canvasvectorrenderer.js b/src/ol/renderer/canvas/canvasvectorrenderer.js
index 8f5b2c6dd4..30d7a3ebca 100644
--- a/src/ol/renderer/canvas/canvasvectorrenderer.js
+++ b/src/ol/renderer/canvas/canvasvectorrenderer.js
@@ -160,7 +160,7 @@ ol.renderer.canvas.Vector.prototype.renderLineStringFeatures_ =
context.beginPath();
for (i = 0, ii = features.length; i < ii; ++i) {
feature = features[i];
- if (feature.renderIntent === ol.layer.VectorLayerRenderIntent.HIDDEN) {
+ if (feature.getRenderIntent() == ol.layer.VectorLayerRenderIntent.HIDDEN) {
continue;
}
id = goog.getUid(feature);
@@ -249,7 +249,7 @@ ol.renderer.canvas.Vector.prototype.renderPointFeatures_ =
context.globalAlpha = alpha;
for (i = 0, ii = features.length; i < ii; ++i) {
feature = features[i];
- if (feature.renderIntent === ol.layer.VectorLayerRenderIntent.HIDDEN) {
+ if (feature.getRenderIntent() == ol.layer.VectorLayerRenderIntent.HIDDEN) {
continue;
}
id = goog.getUid(feature);
@@ -325,7 +325,7 @@ ol.renderer.canvas.Vector.prototype.renderText_ =
for (var i = 0, ii = features.length; i < ii; ++i) {
feature = features[i];
- if (feature.renderIntent === ol.layer.VectorLayerRenderIntent.HIDDEN) {
+ if (feature.getRenderIntent() == ol.layer.VectorLayerRenderIntent.HIDDEN) {
continue;
}
vecs = ol.renderer.canvas.Vector.getLabelVectors(
@@ -393,7 +393,7 @@ ol.renderer.canvas.Vector.prototype.renderPolygonFeatures_ =
context.beginPath();
for (i = 0, ii = features.length; i < ii; ++i) {
feature = features[i];
- if (feature.renderIntent === ol.layer.VectorLayerRenderIntent.HIDDEN) {
+ if (feature.getRenderIntent() == ol.layer.VectorLayerRenderIntent.HIDDEN) {
continue;
}
geometry = feature.getGeometry();
diff --git a/test/spec/ol/expr/expression.test.js b/test/spec/ol/expr/expression.test.js
index 4f0f0942db..35dfa37144 100644
--- a/test/spec/ol/expr/expression.test.js
+++ b/test/spec/ol/expr/expression.test.js
@@ -894,7 +894,7 @@ describe('ol.expr.lib', function() {
describe('renderIntent()', function() {
var feature = new ol.Feature();
- feature.renderIntent = 'foo';
+ feature.setRenderIntent('foo');
var isFoo = parse('renderIntent("foo")');
var isBar = parse('renderIntent("bar")');
diff --git a/test/spec/ol/interaction/selectinteraction.test.js b/test/spec/ol/interaction/selectinteraction.test.js
index 12de3a07a6..cedd9211f1 100644
--- a/test/spec/ol/interaction/selectinteraction.test.js
+++ b/test/spec/ol/interaction/selectinteraction.test.js
@@ -46,29 +46,27 @@ describe('ol.interaction.Select', function() {
describe('#select', function() {
+ var selectedFeaturesFilter = function(feature) {
+ return feature.getRenderIntent() == 'selected';
+ };
+
it('toggles selection of features', function() {
select.select(map, [features], [vector]);
- var layer = select.selectionLayers[goog.getUid(map)]
- .layers[goog.getUid(vector)];
- expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2);
+ expect(vector.getFeatures(selectedFeaturesFilter).length).to.be(2);
select.select(map, [features], [vector]);
- expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(0);
+ expect(vector.getFeatures(selectedFeaturesFilter).length).to.be(0);
});
it('can append features to an existing selection', function() {
- select.select(map, [[features[0]]], [vector]);
+ select.select(map, [[features[0]]], [vector], true);
select.select(map, [[features[1]]], [vector]);
- var layer = select.selectionLayers[goog.getUid(map)]
- .layers[goog.getUid(vector)];
- expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(2);
+ expect(vector.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);
- var layer = select.selectionLayers[goog.getUid(map)]
- .layers[goog.getUid(vector)];
- expect(goog.object.getCount(layer.featureCache_.idLookup_)).to.be(1);
+ expect(vector.getFeatures(selectedFeaturesFilter).length).to.be(1);
});
});
@@ -76,7 +74,6 @@ describe('ol.interaction.Select', function() {
});
goog.require('goog.dispose');
-goog.require('goog.object');
goog.require('ol.Map');
goog.require('ol.interaction.Select');
goog.require('ol.layer.Vector');