Merge pull request #2087 from tschaub/id-index
Add method for retrieving features by id.
This commit is contained in:
@@ -185,6 +185,7 @@ ol.Feature.prototype.setStyle = function(style) {
|
||||
*/
|
||||
ol.Feature.prototype.setId = function(id) {
|
||||
this.id_ = id;
|
||||
this.dispatchChangeEvent();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -69,6 +69,20 @@ ol.source.Vector = function(opt_options) {
|
||||
*/
|
||||
this.nullGeometryFeatures_ = {};
|
||||
|
||||
/**
|
||||
* A lookup of features by id (the return from feature.getId()).
|
||||
* @private
|
||||
* @type {Object.<string, ol.Feature>}
|
||||
*/
|
||||
this.idIndex_ = {};
|
||||
|
||||
/**
|
||||
* A lookup of features without id (keyed by goog.getUid(feature)).
|
||||
* @private
|
||||
* @type {Object.<string, ol.Feature>}
|
||||
*/
|
||||
this.undefIdIndex_ = {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {Object.<string, Array.<goog.events.Key>>}
|
||||
@@ -114,7 +128,18 @@ ol.source.Vector.prototype.addFeatureInternal = function(feature) {
|
||||
var extent = geometry.getExtent();
|
||||
this.rBush_.insert(extent, feature);
|
||||
} else {
|
||||
this.nullGeometryFeatures_[goog.getUid(feature).toString()] = feature;
|
||||
this.nullGeometryFeatures_[featureKey] = feature;
|
||||
}
|
||||
var id = feature.getId();
|
||||
if (goog.isDef(id)) {
|
||||
var sid = id.toString();
|
||||
goog.asserts.assert(!(sid in this.idIndex_),
|
||||
'Feature with same id already added to the source: ' + id);
|
||||
this.idIndex_[sid] = feature;
|
||||
} else {
|
||||
goog.asserts.assert(!(featureKey in this.undefIdIndex_),
|
||||
'Feature already added to the source');
|
||||
this.undefIdIndex_[featureKey] = feature;
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new ol.source.VectorEvent(ol.source.VectorEventType.ADDFEATURE, feature));
|
||||
@@ -314,6 +339,20 @@ ol.source.Vector.prototype.getExtent = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a feature by its identifier (the value returned by feature.getId()).
|
||||
* Note that the index treats string and numeric identifiers as the same. So
|
||||
* `source.getFeatureById(2)` will return a feature with id `'2'` or `2`.
|
||||
*
|
||||
* @param {string|number} id Feature identifier.
|
||||
* @return {ol.Feature} The feature (or `null` if not found).
|
||||
*/
|
||||
ol.source.Vector.prototype.getFeatureById = function(id) {
|
||||
var feature = this.idIndex_[id.toString()];
|
||||
return goog.isDef(feature) ? feature : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {goog.events.Event} event Event.
|
||||
* @private
|
||||
@@ -336,6 +375,35 @@ ol.source.Vector.prototype.handleFeatureChange_ = function(event) {
|
||||
this.rBush_.update(extent, feature);
|
||||
}
|
||||
}
|
||||
var id = feature.getId();
|
||||
var removed;
|
||||
if (goog.isDef(id)) {
|
||||
var sid = id.toString();
|
||||
if (featureKey in this.undefIdIndex_) {
|
||||
delete this.undefIdIndex_[featureKey];
|
||||
goog.asserts.assert(!goog.isDef(this.idIndex_[sid]),
|
||||
'Duplicate feature id: ' + id);
|
||||
this.idIndex_[sid] = feature;
|
||||
} else {
|
||||
if (this.idIndex_[sid] !== feature) {
|
||||
removed = this.removeFromIdIndex_(feature);
|
||||
goog.asserts.assert(removed,
|
||||
'Expected feature to be removed from index');
|
||||
goog.asserts.assert(!(sid in this.idIndex_),
|
||||
'Duplicate feature id: ' + id);
|
||||
this.idIndex_[sid] = feature;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!(featureKey in this.undefIdIndex_)) {
|
||||
removed = this.removeFromIdIndex_(feature);
|
||||
goog.asserts.assert(removed,
|
||||
'Expected feature to be removed from index');
|
||||
this.undefIdIndex_[featureKey] = feature;
|
||||
} else {
|
||||
goog.asserts.assert(this.undefIdIndex_[featureKey] === feature);
|
||||
}
|
||||
}
|
||||
this.dispatchChangeEvent();
|
||||
};
|
||||
|
||||
@@ -384,11 +452,37 @@ ol.source.Vector.prototype.removeFeatureInternal = function(feature) {
|
||||
goog.array.forEach(this.featureChangeKeys_[featureKey],
|
||||
goog.events.unlistenByKey);
|
||||
delete this.featureChangeKeys_[featureKey];
|
||||
var id = feature.getId();
|
||||
if (goog.isDef(id)) {
|
||||
delete this.idIndex_[id.toString()];
|
||||
} else {
|
||||
delete this.undefIdIndex_[featureKey];
|
||||
}
|
||||
this.dispatchEvent(new ol.source.VectorEvent(
|
||||
ol.source.VectorEventType.REMOVEFEATURE, feature));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove a feature from the id index. Called internally when the feature id
|
||||
* may have changed.
|
||||
* @param {ol.Feature} feature The feature.
|
||||
* @return {boolean} Removed the feature from the index.
|
||||
* @private
|
||||
*/
|
||||
ol.source.Vector.prototype.removeFromIdIndex_ = function(feature) {
|
||||
var removed = false;
|
||||
for (var id in this.idIndex_) {
|
||||
if (this.idIndex_[id] === feature) {
|
||||
delete this.idIndex_[id];
|
||||
removed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
|
||||
@@ -232,6 +232,34 @@ describe('ol.Feature', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('#setId()', function() {
|
||||
|
||||
it('sets the feature identifier', function() {
|
||||
var feature = new ol.Feature();
|
||||
expect(feature.getId()).to.be(undefined);
|
||||
feature.setId('foo');
|
||||
expect(feature.getId()).to.be('foo');
|
||||
});
|
||||
|
||||
it('accepts a string or number', function() {
|
||||
var feature = new ol.Feature();
|
||||
feature.setId('foo');
|
||||
expect(feature.getId()).to.be('foo');
|
||||
feature.setId(2);
|
||||
expect(feature.getId()).to.be(2);
|
||||
});
|
||||
|
||||
it('dispatches the "change" event', function(done) {
|
||||
var feature = new ol.Feature();
|
||||
feature.on('change', function() {
|
||||
expect(feature.getId()).to.be('foo');
|
||||
done();
|
||||
});
|
||||
feature.setId('foo');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('#getStyleFunction()', function() {
|
||||
|
||||
var styleFunction = function(resolution) {
|
||||
|
||||
@@ -244,6 +244,140 @@ describe('ol.source.Vector', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('#getFeatureById()', function() {
|
||||
var source;
|
||||
beforeEach(function() {
|
||||
source = new ol.source.Vector();
|
||||
});
|
||||
|
||||
it('returns a feature by id', function() {
|
||||
var feature = new ol.Feature();
|
||||
feature.setId('foo');
|
||||
source.addFeature(feature);
|
||||
expect(source.getFeatureById('foo')).to.be(feature);
|
||||
});
|
||||
|
||||
it('returns a feature by id (set after add)', function() {
|
||||
var feature = new ol.Feature();
|
||||
source.addFeature(feature);
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
feature.setId('foo');
|
||||
expect(source.getFeatureById('foo')).to.be(feature);
|
||||
});
|
||||
|
||||
it('returns null when no feature is found', function() {
|
||||
var feature = new ol.Feature();
|
||||
feature.setId('foo');
|
||||
source.addFeature(feature);
|
||||
expect(source.getFeatureById('bar')).to.be(null);
|
||||
});
|
||||
|
||||
it('returns null after removing feature', function() {
|
||||
var feature = new ol.Feature();
|
||||
feature.setId('foo');
|
||||
source.addFeature(feature);
|
||||
expect(source.getFeatureById('foo')).to.be(feature);
|
||||
source.removeFeature(feature);
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
});
|
||||
|
||||
it('returns null after unsetting id', function() {
|
||||
var feature = new ol.Feature();
|
||||
feature.setId('foo');
|
||||
source.addFeature(feature);
|
||||
expect(source.getFeatureById('foo')).to.be(feature);
|
||||
feature.setId(undefined);
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
});
|
||||
|
||||
it('returns null after clear', function() {
|
||||
var feature = new ol.Feature();
|
||||
feature.setId('foo');
|
||||
source.addFeature(feature);
|
||||
expect(source.getFeatureById('foo')).to.be(feature);
|
||||
source.clear();
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
});
|
||||
|
||||
it('returns null when no features are indexed', function() {
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
source.addFeature(new ol.Feature());
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
});
|
||||
|
||||
it('returns correct feature after add/remove/add', function() {
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
var first = new ol.Feature();
|
||||
first.setId('foo');
|
||||
source.addFeature(first);
|
||||
expect(source.getFeatureById('foo')).to.be(first);
|
||||
source.removeFeature(first);
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
var second = new ol.Feature();
|
||||
second.setId('foo');
|
||||
source.addFeature(second);
|
||||
expect(source.getFeatureById('foo')).to.be(second);
|
||||
});
|
||||
|
||||
it('returns correct feature after add/change', function() {
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
var feature = new ol.Feature();
|
||||
feature.setId('foo');
|
||||
source.addFeature(feature);
|
||||
expect(source.getFeatureById('foo')).to.be(feature);
|
||||
feature.setId('bar');
|
||||
expect(source.getFeatureById('foo')).to.be(null);
|
||||
expect(source.getFeatureById('bar')).to.be(feature);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('the feature id index', function() {
|
||||
var source;
|
||||
beforeEach(function() {
|
||||
source = new ol.source.Vector();
|
||||
});
|
||||
|
||||
it('enforces a uniqueness constraint (on add)', function() {
|
||||
var feature = new ol.Feature();
|
||||
feature.setId('foo');
|
||||
source.addFeature(feature);
|
||||
var dupe = new ol.Feature();
|
||||
dupe.setId('foo');
|
||||
expect(function() {
|
||||
source.addFeature(dupe);
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
it('enforces a uniqueness constraint (on change)', function() {
|
||||
var foo = new ol.Feature();
|
||||
foo.setId('foo');
|
||||
source.addFeature(foo);
|
||||
var bar = new ol.Feature();
|
||||
bar.setId('bar');
|
||||
source.addFeature(bar);
|
||||
expect(function() {
|
||||
bar.setId('foo');
|
||||
}).to.throwException();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('the undefined feature id index', function() {
|
||||
var source;
|
||||
beforeEach(function() {
|
||||
source = new ol.source.Vector();
|
||||
});
|
||||
|
||||
it('disallows adding the same feature twice', function() {
|
||||
var feature = new ol.Feature();
|
||||
source.addFeature(feature);
|
||||
expect(function() {
|
||||
source.addFeature(feature);
|
||||
}).to.throwException();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user