diff --git a/src/ol/layer/vectorlayer.exports b/src/ol/layer/vectorlayer.exports
index 669da99ad1..fa79c6b730 100644
--- a/src/ol/layer/vectorlayer.exports
+++ b/src/ol/layer/vectorlayer.exports
@@ -1,4 +1,3 @@
@exportClass ol.layer.Vector ol.layer.VectorLayerOptions
@exportProperty ol.layer.Vector.prototype.addFeatures
-@exportProperty ol.layer.Vector.prototype.parseFeatures
diff --git a/src/ol/layer/vectorlayer.js b/src/ol/layer/vectorlayer.js
index 264c4c9fa4..fdef16b75a 100644
--- a/src/ol/layer/vectorlayer.js
+++ b/src/ol/layer/vectorlayer.js
@@ -279,34 +279,26 @@ ol.layer.Vector.prototype.getVectorSource = function() {
/**
- * @param {ol.expr.Expression=} opt_expr Expression for filtering.
- * @return {Array.
} Array of features.
- */
-ol.layer.Vector.prototype.getFeatures = function(opt_expr) {
- return goog.object.getValues(
- this.featureCache_.getFeaturesObject(opt_expr));
-};
-
-
-/**
- * @param {ol.expr.Expression=} opt_expr Expression for filtering.
- * @return {Object.} Features.
- */
-ol.layer.Vector.prototype.getFeaturesObject = function(opt_expr) {
- return this.featureCache_.getFeaturesObject(opt_expr);
-};
-
-
-/**
- * Get all features whose bounding box intersects the provided extent.
+ * Get all features whose bounding box intersects the provided extent. This
+ * method is intended for being called by the renderer. When null is returned,
+ * the renderer should not waste time rendering, and `opt_callback` is
+ * usually a function that requests a renderFrame, which will be called as soon
+ * as the data for `extent` is available.
*
* @param {ol.Extent} extent Bounding extent.
+ * @param {ol.Projection} projection Target projection.
* @param {ol.geom.GeometryType=} opt_type Optional geometry type.
- * @return {Object.} Features.
+ * @param {Function=} opt_callback Callback to call when data is parsed.
+ * @return {Object.} Features or null if source is loading
+ * data for `extent`.
*/
ol.layer.Vector.prototype.getFeaturesObjectForExtent = function(extent,
- opt_type) {
- return this.featureCache_.getFeaturesObjectForExtent(extent, opt_type);
+ projection, opt_type, opt_callback) {
+ var source = this.getSource();
+ return source.prepareFeatures(this, extent, projection, opt_callback) ==
+ ol.source.VectorLoadState.LOADING ?
+ null :
+ this.featureCache_.getFeaturesObjectForExtent(extent, opt_type);
};
diff --git a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
index 343d783f72..a211bd4786 100644
--- a/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
+++ b/src/ol/renderer/canvas/canvasvectorlayerrenderer.js
@@ -228,9 +228,12 @@ ol.renderer.canvas.VectorLayer.prototype.getFeatureInfoForPixel =
* @param {function(Array., ol.layer.Layer)} success Callback for
* successful queries. The passed arguments are the resulting features
* and the layer.
+ * @param {function(Object)=} opt_error Callback for
+ * unsuccessful queries.
*/
ol.renderer.canvas.VectorLayer.prototype.getFeaturesForPixel =
- function(pixel, success) {
+ function(pixel, success, opt_error) {
+ // TODO What do we want to pass to the error callback?
var map = this.getMap();
var result = [];
@@ -247,7 +250,15 @@ ol.renderer.canvas.VectorLayer.prototype.getFeaturesForPixel =
var locationMin = [location[0] - halfMaxWidth, location[1] - halfMaxHeight];
var locationMax = [location[0] + halfMaxWidth, location[1] + halfMaxHeight];
var locationBbox = ol.extent.boundingExtent([locationMin, locationMax]);
- var candidates = layer.getFeaturesObjectForExtent(locationBbox);
+ var candidates = layer.getFeaturesObjectForExtent(locationBbox,
+ map.getView().getView2D().getProjection());
+ if (goog.isNull(candidates)) {
+ // data is not loaded
+ if (goog.isDef(opt_error)) {
+ goog.global.setTimeout(function() { opt_error({}); }, 0);
+ }
+ return;
+ }
var candidate, geom, type, symbolBounds, symbolSize, halfWidth, halfHeight,
coordinates, j;
@@ -446,6 +457,7 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
dirty = false,
i, type, tileExtent,
groups, group, j, numGroups, featuresObject, tileHasFeatures;
+ fetchTileData:
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
tileCoord = new ol.TileCoord(0, x, y);
@@ -464,7 +476,12 @@ ol.renderer.canvas.VectorLayer.prototype.renderFrame =
if (!goog.isDef(featuresToRender[type])) {
featuresToRender[type] = {};
}
- featuresObject = layer.getFeaturesObjectForExtent(tileExtent, type);
+ featuresObject = layer.getFeaturesObjectForExtent(tileExtent,
+ projection, type, this.requestMapRenderFrame_);
+ if (goog.isNull(featuresObject)) {
+ deferred = true;
+ break fetchTileData;
+ }
tileHasFeatures = tileHasFeatures ||
!goog.object.isEmpty(featuresObject);
goog.object.extend(featuresToRender[type], featuresObject);
diff --git a/test/spec/ol/layer/vectorlayer.test.js b/test/spec/ol/layer/vectorlayer.test.js
index 1525617d6a..8c6c9f06e1 100644
--- a/test/spec/ol/layer/vectorlayer.test.js
+++ b/test/spec/ol/layer/vectorlayer.test.js
@@ -9,11 +9,12 @@ describe('ol.layer.Vector', function() {
source: new ol.source.Vector({})
});
layer.addFeatures([new ol.Feature(), new ol.Feature()]);
- expect(layer.getFeatures().length).to.eql(2);
+ expect(goog.object.getCount(layer.featureCache_.getFeaturesObject()))
+ .to.eql(2);
});
});
- describe('#getFeatures()', function() {
+ describe('ol.layer.FeatureCache#getFeaturesObject()', function() {
var layer, features;
@@ -55,18 +56,18 @@ describe('ol.layer.Vector', function() {
it('can filter by geometry type using its GeometryType index', function() {
sinon.spy(geomFilter, 'evaluate');
- var lineStrings = layer.getFeatures(geomFilter);
+ var lineStrings = layer.featureCache_.getFeaturesObject(geomFilter);
expect(geomFilter.evaluate).to.not.be.called();
- expect(lineStrings.length).to.eql(4);
- expect(lineStrings).to.contain(features[4]);
+ expect(goog.object.getCount(lineStrings)).to.eql(4);
+ expect(goog.object.getValues(lineStrings)).to.contain(features[4]);
});
it('can filter by extent using its RTree', function() {
sinon.spy(extentFilter, 'evaluate');
- var subset = layer.getFeatures(extentFilter);
+ var subset = layer.featureCache_.getFeaturesObject(extentFilter);
expect(extentFilter.evaluate).to.not.be.called();
- expect(subset.length).to.eql(4);
- expect(subset).not.to.contain(features[7]);
+ expect(goog.object.getCount(subset)).to.eql(4);
+ expect(goog.object.getValues(subset)).not.to.contain(features[7]);
});
it('can filter by extent and geometry type using its index', function() {
@@ -76,21 +77,21 @@ describe('ol.layer.Vector', function() {
ol.expr.LogicalOp.AND, extentFilter, geomFilter);
sinon.spy(filter1, 'evaluate');
sinon.spy(filter2, 'evaluate');
- var subset1 = layer.getFeatures(filter1);
- var subset2 = layer.getFeatures(filter2);
+ var subset1 = layer.featureCache_.getFeaturesObject(filter1);
+ var subset2 = layer.featureCache_.getFeaturesObject(filter2);
expect(filter1.evaluate).to.not.be.called();
expect(filter2.evaluate).to.not.be.called();
- expect(subset1.length).to.eql(0);
- expect(subset2.length).to.eql(0);
+ expect(goog.object.getCount(subset1)).to.eql(0);
+ expect(goog.object.getCount(subset2)).to.eql(0);
});
it('can handle query using the filter\'s evaluate function', function() {
var filter = new ol.expr.Logical(
ol.expr.LogicalOp.OR, geomFilter, extentFilter);
sinon.spy(filter, 'evaluate');
- var subset = layer.getFeatures(filter);
+ var subset = layer.featureCache_.getFeaturesObject(filter);
expect(filter.evaluate).to.be.called();
- expect(subset.length).to.eql(8);
+ expect(goog.object.getCount(subset)).to.eql(8);
});
});
@@ -177,6 +178,7 @@ describe('ol.layer.Vector', function() {
});
goog.require('goog.dispose');
+goog.require('goog.object');
goog.require('ol.Feature');
goog.require('ol.expr');
goog.require('ol.expr.Logical');