Alternatively manage features in an ol.Collection
ol.layer.Vector can now manage both an RTree and a Collection of features. The new useSpatialIndex option allows to opt out of RTree management, and the new ol.Collection type of the features option allows to opt in for Collection management.
This commit is contained in:
@@ -4977,12 +4977,13 @@ olx.source.TileWMSOptions.prototype.wrapX;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
||||||
* features: (Array.<ol.Feature>|undefined),
|
* features: (Array.<ol.Feature>|ol.Collection.<ol.Feature>|undefined),
|
||||||
* format: (ol.format.Feature|undefined),
|
* format: (ol.format.Feature|undefined),
|
||||||
* loader: (ol.FeatureLoader|undefined),
|
* loader: (ol.FeatureLoader|undefined),
|
||||||
* logo: (string|olx.LogoOptions|undefined),
|
* logo: (string|olx.LogoOptions|undefined),
|
||||||
* strategy: (ol.LoadingStrategy|undefined),
|
* strategy: (ol.LoadingStrategy|undefined),
|
||||||
* url: (string|undefined),
|
* url: (string|undefined),
|
||||||
|
* useSpatialIndex: (boolean|undefined),
|
||||||
* wrapX: (boolean|undefined)}}
|
* wrapX: (boolean|undefined)}}
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
@@ -4998,8 +4999,9 @@ olx.source.VectorOptions.prototype.attributions;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Features.
|
* Features. If provided as {@link ol.Collection}, the features in the source
|
||||||
* @type {Array.<ol.Feature>|undefined}
|
* and the collection will stay in sync.
|
||||||
|
* @type {Array.<ol.Feature>|ol.Collection.<ol.Feature>|undefined}
|
||||||
* @api stable
|
* @api stable
|
||||||
*/
|
*/
|
||||||
olx.source.VectorOptions.prototype.features;
|
olx.source.VectorOptions.prototype.features;
|
||||||
@@ -5052,6 +5054,29 @@ olx.source.VectorOptions.prototype.strategy;
|
|||||||
olx.source.VectorOptions.prototype.url;
|
olx.source.VectorOptions.prototype.url;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, an RTree is used as spatial index. When features are removed and
|
||||||
|
* added frequently, and the total number of features is low, setting this to
|
||||||
|
* `false` may improve performance.
|
||||||
|
*
|
||||||
|
* Note that
|
||||||
|
* {@link ol.source.Vector#getFeaturesInExtent},
|
||||||
|
* {@link ol.source.Vector#getClosestFeatureToCoordinate} and
|
||||||
|
* {@link ol.source.Vector#getExtent} cannot be used when `useSpatialIndex` is
|
||||||
|
* set to `false`, and {@link ol.source.Vector#forEachFeatureInExtent} will loop
|
||||||
|
* through all features.
|
||||||
|
*
|
||||||
|
* When set to `false`, the features will be maintained in an
|
||||||
|
* {@link ol.Collection}, which can be retrieved through
|
||||||
|
* {@link ol.source.Vector#getFeaturesCollection}.
|
||||||
|
*
|
||||||
|
* The default is `true`.
|
||||||
|
* @type {boolean|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.VectorOptions.prototype.useSpatialIndex;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap the world horizontally. Default is `true`. For vector editing across the
|
* Wrap the world horizontally. Default is `true`. For vector editing across the
|
||||||
* -180° and 180° meridians to work properly, this should be set to `false`. The
|
* -180° and 180° meridians to work properly, this should be set to `false`. The
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// FIXME bulk feature upload - suppress events
|
// FIXME bulk feature upload - suppress events
|
||||||
// FIXME put features in an ol.Collection
|
|
||||||
// FIXME make change-detection more refined (notably, geometry hint)
|
// FIXME make change-detection more refined (notably, geometry hint)
|
||||||
|
|
||||||
goog.provide('ol.source.Vector');
|
goog.provide('ol.source.Vector');
|
||||||
@@ -12,7 +11,10 @@ goog.require('goog.events');
|
|||||||
goog.require('goog.events.Event');
|
goog.require('goog.events.Event');
|
||||||
goog.require('goog.events.EventType');
|
goog.require('goog.events.EventType');
|
||||||
goog.require('goog.object');
|
goog.require('goog.object');
|
||||||
|
goog.require('ol.Collection');
|
||||||
|
goog.require('ol.CollectionEventType');
|
||||||
goog.require('ol.Extent');
|
goog.require('ol.Extent');
|
||||||
|
goog.require('ol.Feature');
|
||||||
goog.require('ol.FeatureLoader');
|
goog.require('ol.FeatureLoader');
|
||||||
goog.require('ol.LoadingStrategy');
|
goog.require('ol.LoadingStrategy');
|
||||||
goog.require('ol.ObjectEventType');
|
goog.require('ol.ObjectEventType');
|
||||||
@@ -105,11 +107,14 @@ ol.source.Vector = function(opt_options) {
|
|||||||
this.strategy_ = goog.isDef(options.strategy) ? options.strategy :
|
this.strategy_ = goog.isDef(options.strategy) ? options.strategy :
|
||||||
ol.loadingstrategy.all;
|
ol.loadingstrategy.all;
|
||||||
|
|
||||||
|
var useSpatialIndex =
|
||||||
|
goog.isDef(options.useSpatialIndex) ? options.useSpatialIndex : true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {ol.structs.RBush.<ol.Feature>}
|
* @type {ol.structs.RBush.<ol.Feature>}
|
||||||
*/
|
*/
|
||||||
this.featuresRtree_ = new ol.structs.RBush();
|
this.featuresRtree_ = useSpatialIndex ? new ol.structs.RBush() : null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@@ -143,8 +148,27 @@ ol.source.Vector = function(opt_options) {
|
|||||||
*/
|
*/
|
||||||
this.featureChangeKeys_ = {};
|
this.featureChangeKeys_ = {};
|
||||||
|
|
||||||
if (goog.isDef(options.features)) {
|
/**
|
||||||
this.addFeaturesInternal(options.features);
|
* @private
|
||||||
|
* @type {ol.Collection.<ol.Feature>}
|
||||||
|
*/
|
||||||
|
this.featuresCollection_ = null;
|
||||||
|
|
||||||
|
var collection, features;
|
||||||
|
if (options.features instanceof ol.Collection) {
|
||||||
|
collection = options.features;
|
||||||
|
features = collection.getArray();
|
||||||
|
} else if (goog.isArray(options.features)) {
|
||||||
|
features = options.features;
|
||||||
|
}
|
||||||
|
if (!useSpatialIndex && !goog.isDef(collection)) {
|
||||||
|
collection = new ol.Collection(features);
|
||||||
|
}
|
||||||
|
if (goog.isDef(features)) {
|
||||||
|
this.addFeaturesInternal(features);
|
||||||
|
}
|
||||||
|
if (goog.isDef(collection)) {
|
||||||
|
this.bindFeaturesCollection_(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -181,7 +205,9 @@ ol.source.Vector.prototype.addFeatureInternal = function(feature) {
|
|||||||
var geometry = feature.getGeometry();
|
var geometry = feature.getGeometry();
|
||||||
if (goog.isDefAndNotNull(geometry)) {
|
if (goog.isDefAndNotNull(geometry)) {
|
||||||
var extent = geometry.getExtent();
|
var extent = geometry.getExtent();
|
||||||
this.featuresRtree_.insert(extent, feature);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
this.featuresRtree_.insert(extent, feature);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.nullGeometryFeatures_[featureKey] = feature;
|
this.nullGeometryFeatures_[featureKey] = feature;
|
||||||
}
|
}
|
||||||
@@ -280,7 +306,9 @@ ol.source.Vector.prototype.addFeaturesInternal = function(features) {
|
|||||||
this.nullGeometryFeatures_[featureKey] = feature;
|
this.nullGeometryFeatures_[featureKey] = feature;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.featuresRtree_.load(extents, geometryFeatures);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
this.featuresRtree_.load(extents, geometryFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0, length = newFeatures.length; i < length; i++) {
|
for (i = 0, length = newFeatures.length; i < length; i++) {
|
||||||
this.dispatchEvent(new ol.source.VectorEvent(
|
this.dispatchEvent(new ol.source.VectorEvent(
|
||||||
@@ -289,6 +317,54 @@ ol.source.Vector.prototype.addFeaturesInternal = function(features) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {!ol.Collection.<ol.Feature>} collection Collection.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.source.Vector.prototype.bindFeaturesCollection_ = function(collection) {
|
||||||
|
goog.asserts.assert(goog.isNull(this.featuresCollection_),
|
||||||
|
'bindFeaturesCollection can only be called once');
|
||||||
|
var modifyingCollection = false;
|
||||||
|
goog.events.listen(this, ol.source.VectorEventType.ADDFEATURE,
|
||||||
|
function(evt) {
|
||||||
|
if (!modifyingCollection) {
|
||||||
|
modifyingCollection = true;
|
||||||
|
collection.push(evt.feature);
|
||||||
|
modifyingCollection = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
goog.events.listen(this, ol.source.VectorEventType.REMOVEFEATURE,
|
||||||
|
function(evt) {
|
||||||
|
if (!modifyingCollection) {
|
||||||
|
modifyingCollection = true;
|
||||||
|
collection.remove(evt.feature);
|
||||||
|
modifyingCollection = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
goog.events.listen(collection, ol.CollectionEventType.ADD,
|
||||||
|
function(evt) {
|
||||||
|
if (!modifyingCollection) {
|
||||||
|
var feature = evt.element;
|
||||||
|
goog.asserts.assertInstanceof(feature, ol.Feature);
|
||||||
|
modifyingCollection = true;
|
||||||
|
this.addFeature(feature);
|
||||||
|
modifyingCollection = false;
|
||||||
|
}
|
||||||
|
}, false, this);
|
||||||
|
goog.events.listen(collection, ol.CollectionEventType.REMOVE,
|
||||||
|
function(evt) {
|
||||||
|
if (!modifyingCollection) {
|
||||||
|
var feature = evt.element;
|
||||||
|
goog.asserts.assertInstanceof(feature, ol.Feature);
|
||||||
|
modifyingCollection = true;
|
||||||
|
this.removeFeature(feature);
|
||||||
|
modifyingCollection = false;
|
||||||
|
}
|
||||||
|
}, false, this);
|
||||||
|
this.featuresCollection_ = collection;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all features from the source.
|
* Remove all features from the source.
|
||||||
* @param {boolean=} opt_fast Skip dispatching of {@link removefeature} events.
|
* @param {boolean=} opt_fast Skip dispatching of {@link removefeature} events.
|
||||||
@@ -305,8 +381,10 @@ ol.source.Vector.prototype.clear = function(opt_fast) {
|
|||||||
this.undefIdIndex_ = {};
|
this.undefIdIndex_ = {};
|
||||||
} else {
|
} else {
|
||||||
var rmFeatureInternal = this.removeFeatureInternal;
|
var rmFeatureInternal = this.removeFeatureInternal;
|
||||||
this.featuresRtree_.forEach(rmFeatureInternal, this);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
goog.object.forEach(this.nullGeometryFeatures_, rmFeatureInternal, this);
|
this.featuresRtree_.forEach(rmFeatureInternal, this);
|
||||||
|
goog.object.forEach(this.nullGeometryFeatures_, rmFeatureInternal, this);
|
||||||
|
}
|
||||||
goog.asserts.assert(goog.object.isEmpty(this.featureChangeKeys_),
|
goog.asserts.assert(goog.object.isEmpty(this.featureChangeKeys_),
|
||||||
'featureChangeKeys is an empty object now');
|
'featureChangeKeys is an empty object now');
|
||||||
goog.asserts.assert(goog.object.isEmpty(this.idIndex_),
|
goog.asserts.assert(goog.object.isEmpty(this.idIndex_),
|
||||||
@@ -315,7 +393,9 @@ ol.source.Vector.prototype.clear = function(opt_fast) {
|
|||||||
'undefIdIndex is an empty object now');
|
'undefIdIndex is an empty object now');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.featuresRtree_.clear();
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
this.featuresRtree_.clear();
|
||||||
|
}
|
||||||
this.loadedExtentsRtree_.clear();
|
this.loadedExtentsRtree_.clear();
|
||||||
this.nullGeometryFeatures_ = {};
|
this.nullGeometryFeatures_ = {};
|
||||||
|
|
||||||
@@ -338,7 +418,11 @@ ol.source.Vector.prototype.clear = function(opt_fast) {
|
|||||||
* @api stable
|
* @api stable
|
||||||
*/
|
*/
|
||||||
ol.source.Vector.prototype.forEachFeature = function(callback, opt_this) {
|
ol.source.Vector.prototype.forEachFeature = function(callback, opt_this) {
|
||||||
return this.featuresRtree_.forEach(callback, opt_this);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
return this.featuresRtree_.forEach(callback, opt_this);
|
||||||
|
} else if (!goog.isNull(this.featuresCollection_)) {
|
||||||
|
return this.featuresCollection_.forEach(callback, opt_this);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -381,6 +465,9 @@ ol.source.Vector.prototype.forEachFeatureAtCoordinateDirect =
|
|||||||
* the {@link ol.source.Vector#forEachFeatureIntersectingExtent
|
* the {@link ol.source.Vector#forEachFeatureIntersectingExtent
|
||||||
* source.forEachFeatureIntersectingExtent()} method instead.
|
* source.forEachFeatureIntersectingExtent()} method instead.
|
||||||
*
|
*
|
||||||
|
* When `useSpatialIndex` is set to false, this method will loop through all
|
||||||
|
* features, equivalent to {@link ol.source.Vector#forEachFeature}.
|
||||||
|
*
|
||||||
* @param {ol.Extent} extent Extent.
|
* @param {ol.Extent} extent Extent.
|
||||||
* @param {function(this: T, ol.Feature): S} callback Called with each feature
|
* @param {function(this: T, ol.Feature): S} callback Called with each feature
|
||||||
* whose bounding box intersects the provided extent.
|
* whose bounding box intersects the provided extent.
|
||||||
@@ -391,7 +478,11 @@ ol.source.Vector.prototype.forEachFeatureAtCoordinateDirect =
|
|||||||
*/
|
*/
|
||||||
ol.source.Vector.prototype.forEachFeatureInExtent =
|
ol.source.Vector.prototype.forEachFeatureInExtent =
|
||||||
function(extent, callback, opt_this) {
|
function(extent, callback, opt_this) {
|
||||||
return this.featuresRtree_.forEachInExtent(extent, callback, opt_this);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
return this.featuresRtree_.forEachInExtent(extent, callback, opt_this);
|
||||||
|
} else if (!goog.isNull(this.featuresCollection_)) {
|
||||||
|
return this.featuresCollection_.forEach(callback, opt_this);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -448,17 +539,36 @@ ol.source.Vector.prototype.forEachFeatureIntersectingExtent =
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the features collection associated with this source. Will be `null`
|
||||||
|
* unless the source was configured with `useSpatialIndex` set to `false`, or
|
||||||
|
* with an {@link ol.Collection} as `features`.
|
||||||
|
* @return {ol.Collection.<ol.Feature>}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
ol.source.Vector.prototype.getFeaturesCollection = function() {
|
||||||
|
return this.featuresCollection_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all features on the source.
|
* Get all features on the source.
|
||||||
* @return {Array.<ol.Feature>} Features.
|
* @return {Array.<ol.Feature>} Features.
|
||||||
* @api stable
|
* @api stable
|
||||||
*/
|
*/
|
||||||
ol.source.Vector.prototype.getFeatures = function() {
|
ol.source.Vector.prototype.getFeatures = function() {
|
||||||
var features = this.featuresRtree_.getAll();
|
var features;
|
||||||
if (!goog.object.isEmpty(this.nullGeometryFeatures_)) {
|
if (!goog.isNull(this.featuresCollection_)) {
|
||||||
goog.array.extend(
|
features = this.featuresCollection_.getArray();
|
||||||
features, goog.object.getValues(this.nullGeometryFeatures_));
|
} else if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
features = this.featuresRtree_.getAll();
|
||||||
|
if (!goog.object.isEmpty(this.nullGeometryFeatures_)) {
|
||||||
|
goog.array.extend(
|
||||||
|
features, goog.object.getValues(this.nullGeometryFeatures_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
goog.asserts.assert(goog.isDef(features),
|
||||||
|
'Neither featuresRtree_ nor featuresCollection_ are available');
|
||||||
return features;
|
return features;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -482,17 +592,25 @@ ol.source.Vector.prototype.getFeaturesAtCoordinate = function(coordinate) {
|
|||||||
* Get all features in the provided extent. Note that this returns all features
|
* Get all features in the provided extent. Note that this returns all features
|
||||||
* whose bounding boxes intersect the given extent (so it may include features
|
* whose bounding boxes intersect the given extent (so it may include features
|
||||||
* whose geometries do not intersect the extent).
|
* whose geometries do not intersect the extent).
|
||||||
|
*
|
||||||
|
* This method is not available when the source is configured with
|
||||||
|
* `useSpatialIndex` set to `false`.
|
||||||
* @param {ol.Extent} extent Extent.
|
* @param {ol.Extent} extent Extent.
|
||||||
* @return {Array.<ol.Feature>} Features.
|
* @return {Array.<ol.Feature>} Features.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
ol.source.Vector.prototype.getFeaturesInExtent = function(extent) {
|
ol.source.Vector.prototype.getFeaturesInExtent = function(extent) {
|
||||||
|
goog.asserts.assert(!goog.isNull(this.featuresRtree_),
|
||||||
|
'getFeaturesInExtent does not work when useSpatialIndex is set to false');
|
||||||
return this.featuresRtree_.getInExtent(extent);
|
return this.featuresRtree_.getInExtent(extent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the closest feature to the provided coordinate.
|
* Get the closest feature to the provided coordinate.
|
||||||
|
*
|
||||||
|
* This method is not available when the source is configured with
|
||||||
|
* `useSpatialIndex` set to `false`.
|
||||||
* @param {ol.Coordinate} coordinate Coordinate.
|
* @param {ol.Coordinate} coordinate Coordinate.
|
||||||
* @return {ol.Feature} Closest feature.
|
* @return {ol.Feature} Closest feature.
|
||||||
* @api stable
|
* @api stable
|
||||||
@@ -512,6 +630,9 @@ ol.source.Vector.prototype.getClosestFeatureToCoordinate =
|
|||||||
var closestPoint = [NaN, NaN];
|
var closestPoint = [NaN, NaN];
|
||||||
var minSquaredDistance = Infinity;
|
var minSquaredDistance = Infinity;
|
||||||
var extent = [-Infinity, -Infinity, Infinity, Infinity];
|
var extent = [-Infinity, -Infinity, Infinity, Infinity];
|
||||||
|
goog.asserts.assert(!goog.isNull(this.featuresRtree_),
|
||||||
|
'getClosestFeatureToCoordinate does not work with useSpatialIndex set ' +
|
||||||
|
'to false');
|
||||||
this.featuresRtree_.forEachInExtent(extent,
|
this.featuresRtree_.forEachInExtent(extent,
|
||||||
/**
|
/**
|
||||||
* @param {ol.Feature} feature Feature.
|
* @param {ol.Feature} feature Feature.
|
||||||
@@ -542,10 +663,15 @@ ol.source.Vector.prototype.getClosestFeatureToCoordinate =
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the extent of the features currently in the source.
|
* Get the extent of the features currently in the source.
|
||||||
|
*
|
||||||
|
* This method is not available when the source is configured with
|
||||||
|
* `useSpatialIndex` set to `false`.
|
||||||
* @return {ol.Extent} Extent.
|
* @return {ol.Extent} Extent.
|
||||||
* @api stable
|
* @api stable
|
||||||
*/
|
*/
|
||||||
ol.source.Vector.prototype.getExtent = function() {
|
ol.source.Vector.prototype.getExtent = function() {
|
||||||
|
goog.asserts.assert(!goog.isNull(this.featuresRtree_),
|
||||||
|
'getExtent does not work when useSpatialIndex is set to false');
|
||||||
return this.featuresRtree_.getExtent();
|
return this.featuresRtree_.getExtent();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -575,16 +701,22 @@ ol.source.Vector.prototype.handleFeatureChange_ = function(event) {
|
|||||||
var geometry = feature.getGeometry();
|
var geometry = feature.getGeometry();
|
||||||
if (!goog.isDefAndNotNull(geometry)) {
|
if (!goog.isDefAndNotNull(geometry)) {
|
||||||
if (!(featureKey in this.nullGeometryFeatures_)) {
|
if (!(featureKey in this.nullGeometryFeatures_)) {
|
||||||
this.featuresRtree_.remove(feature);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
this.featuresRtree_.remove(feature);
|
||||||
|
}
|
||||||
this.nullGeometryFeatures_[featureKey] = feature;
|
this.nullGeometryFeatures_[featureKey] = feature;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var extent = geometry.getExtent();
|
var extent = geometry.getExtent();
|
||||||
if (featureKey in this.nullGeometryFeatures_) {
|
if (featureKey in this.nullGeometryFeatures_) {
|
||||||
delete this.nullGeometryFeatures_[featureKey];
|
delete this.nullGeometryFeatures_[featureKey];
|
||||||
this.featuresRtree_.insert(extent, feature);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
this.featuresRtree_.insert(extent, feature);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.featuresRtree_.update(extent, feature);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
this.featuresRtree_.update(extent, feature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var id = feature.getId();
|
var id = feature.getId();
|
||||||
@@ -668,7 +800,9 @@ ol.source.Vector.prototype.removeFeature = function(feature) {
|
|||||||
if (featureKey in this.nullGeometryFeatures_) {
|
if (featureKey in this.nullGeometryFeatures_) {
|
||||||
delete this.nullGeometryFeatures_[featureKey];
|
delete this.nullGeometryFeatures_[featureKey];
|
||||||
} else {
|
} else {
|
||||||
this.featuresRtree_.remove(feature);
|
if (!goog.isNull(this.featuresRtree_)) {
|
||||||
|
this.featuresRtree_.remove(feature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.removeFeatureInternal(feature);
|
this.removeFeatureInternal(feature);
|
||||||
this.changed();
|
this.changed();
|
||||||
|
|||||||
@@ -427,10 +427,69 @@ describe('ol.source.Vector', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('with useSpatialIndex set to false', function() {
|
||||||
|
var source;
|
||||||
|
beforeEach(function() {
|
||||||
|
source = new ol.source.Vector({useSpatialIndex: false});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a features collection', function() {
|
||||||
|
expect(source.getFeaturesCollection()).to.be.a(ol.Collection);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#forEachFeatureInExtent loops through all features', function() {
|
||||||
|
source.addFeatures([new ol.Feature(), new ol.Feature()]);
|
||||||
|
var spy = sinon.spy();
|
||||||
|
source.forEachFeatureInExtent([0, 0, 0, 0], spy);
|
||||||
|
expect(spy.callCount).to.be(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a collection of features', function() {
|
||||||
|
var collection, source;
|
||||||
|
beforeEach(function() {
|
||||||
|
collection = new ol.Collection();
|
||||||
|
source = new ol.source.Vector({
|
||||||
|
features: collection
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('#getFeaturesCollection returns the configured collection', function() {
|
||||||
|
expect(source.getFeaturesCollection()).to.equal(collection);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the collection in sync with the source\'s features', function() {
|
||||||
|
var feature = new ol.Feature();
|
||||||
|
source.addFeature(feature);
|
||||||
|
expect(collection.getLength()).to.be(1);
|
||||||
|
source.removeFeature(feature);
|
||||||
|
expect(collection.getLength()).to.be(0);
|
||||||
|
source.addFeatures([feature]);
|
||||||
|
expect(collection.getLength()).to.be(1);
|
||||||
|
source.clear();
|
||||||
|
expect(collection.getLength()).to.be(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the source\'s features in sync with the collection', function() {
|
||||||
|
var feature = new ol.Feature();
|
||||||
|
collection.push(feature);
|
||||||
|
expect(source.getFeatures().length).to.be(1);
|
||||||
|
collection.remove(feature);
|
||||||
|
expect(source.getFeatures().length).to.be(0);
|
||||||
|
collection.extend([feature]);
|
||||||
|
expect(source.getFeatures().length).to.be(1);
|
||||||
|
collection.clear();
|
||||||
|
expect(source.getFeatures().length).to.be(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
goog.require('goog.events');
|
goog.require('goog.events');
|
||||||
|
goog.require('ol.Collection');
|
||||||
goog.require('ol.Feature');
|
goog.require('ol.Feature');
|
||||||
goog.require('ol.geom.Point');
|
goog.require('ol.geom.Point');
|
||||||
goog.require('ol.proj');
|
goog.require('ol.proj');
|
||||||
|
|||||||
Reference in New Issue
Block a user