From 1bafab49f5a747924e92626d109067f647836da3 Mon Sep 17 00:00:00 2001 From: Simon Seyock Date: Tue, 6 Oct 2020 12:16:15 +0200 Subject: [PATCH] Added FEATURELOADSTART, FEATURELOADEND and FEATURELOADERROR events. --- src/ol/featureloader.js | 15 +++--- src/ol/source/Vector.js | 34 ++++++++++++- src/ol/source/VectorEventType.js | 21 ++++++++ test/spec/ol/featureloader.test.js | 30 ++++++++++-- test/spec/ol/source/vector.test.js | 77 ++++++++++++++++++++++++++++++ 5 files changed, 165 insertions(+), 12 deletions(-) diff --git a/src/ol/featureloader.js b/src/ol/featureloader.js index 97b7072dce..6247be210d 100644 --- a/src/ol/featureloader.js +++ b/src/ol/featureloader.js @@ -27,7 +27,7 @@ let withCredentials = false; * import("./extent.js").Extent, * number, * import("./proj/Projection.js").default, - * function(): void=, + * function(Array): void=, * function(): void=): void} FeatureLoader * @api */ @@ -134,14 +134,14 @@ export function xhr(url, format) { * @param {import("./extent.js").Extent} extent Extent. * @param {number} resolution Resolution. * @param {import("./proj/Projection.js").default} projection Projection. - * @param {function(): void} success Success + * @param {function(): void=} success Success * Function called when loading succeeded. - * @param {function(): void} failure Failure + * @param {function(): void=} failure Failure * Function called when loading failed. * @this {import("./source/Vector").default} */ return function (extent, resolution, projection, success, failure) { - const sourceOrTile = /** @type {import("./source/Vector").default} */ (this); + const source = /** @type {import("./source/Vector").default} */ (this); loadFeaturesXhr( url, format, @@ -154,9 +154,12 @@ export function xhr(url, format) { * projection. */ function (features, dataProjection) { - sourceOrTile.addFeatures(features); + if (success !== undefined) { + success(features); + } + source.addFeatures(features); }, - /* FIXME handle error */ VOID + /* FIXME handle error */ failure ? failure : VOID ); }; } diff --git a/src/ol/source/Vector.js b/src/ol/source/Vector.js index 2a6ce00375..e8dff54516 100644 --- a/src/ol/source/Vector.js +++ b/src/ol/source/Vector.js @@ -40,8 +40,9 @@ export class VectorSourceEvent extends Event { /** * @param {string} type Type. * @param {import("../Feature.js").default=} opt_feature Feature. + * @param {Array>=} opt_features Features. */ - constructor(type, opt_feature) { + constructor(type, opt_feature, opt_features) { super(type); /** @@ -50,6 +51,13 @@ export class VectorSourceEvent extends Event { * @api */ this.feature = opt_feature; + + /** + * The features being loaded. + * @type {Array>} + * @api + */ + this.features = opt_features; } } @@ -904,7 +912,29 @@ class VectorSource extends Source { } ); if (!alreadyLoaded) { - this.loader_.call(this, extentToLoad, resolution, projection); + this.dispatchEvent( + new VectorSourceEvent(VectorEventType.FEATURESLOADSTART) + ); + this.loader_.call( + this, + extentToLoad, + resolution, + projection, + function (features) { + this.dispatchEvent( + new VectorSourceEvent( + VectorEventType.FEATURESLOADEND, + undefined, + features + ) + ); + }.bind(this), + function () { + this.dispatchEvent( + new VectorSourceEvent(VectorEventType.FEATURESLOADERROR) + ); + }.bind(this) + ); loadedExtentsRtree.insert(extentToLoad, {extent: extentToLoad.slice()}); this.loading = this.loader_ !== VOID; } diff --git a/src/ol/source/VectorEventType.js b/src/ol/source/VectorEventType.js index ef399e18ed..7cf9e9e9eb 100644 --- a/src/ol/source/VectorEventType.js +++ b/src/ol/source/VectorEventType.js @@ -34,4 +34,25 @@ export default { * @api */ REMOVEFEATURE: 'removefeature', + + /** + * Triggered when features starts loading. + * @event module:ol/source/Vector.VectorSourceEvent#featureloadstart + * @api + */ + FEATURESLOADSTART: 'featuresloadstart', + + /** + * Triggered when features finishes loading. + * @event module:ol/source/Vector.VectorSourceEvent#featureloadend + * @api + */ + FEATURESLOADEND: 'featuresloadend', + + /** + * Triggered if feature loading results in an error. + * @event module:ol/source/Vector.VectorSourceEvent#featureloaderror + * @api + */ + FEATURESLOADERROR: 'featuresloaderror', }; diff --git a/test/spec/ol/featureloader.test.js b/test/spec/ol/featureloader.test.js index 3849d03df6..14fdd90072 100644 --- a/test/spec/ol/featureloader.test.js +++ b/test/spec/ol/featureloader.test.js @@ -19,8 +19,10 @@ describe('ol.featureloader', function () { it('adds features to the source', function (done) { loader = xhr(url, format); source.on('addfeature', function (e) { - expect(source.getFeatures().length).to.be.greaterThan(0); - done(); + setTimeout(function () { + expect(source.getFeatures().length).to.be.greaterThan(0); + done(); + }, 0); }); loader.call(source, [], 1, 'EPSG:3857'); }); @@ -33,8 +35,10 @@ describe('ol.featureloader', function () { loader = xhr(url, format); source.on('addfeature', function (e) { - expect(source.getFeatures().length).to.be.greaterThan(0); - done(); + setTimeout(function () { + expect(source.getFeatures().length).to.be.greaterThan(0); + done(); + }, 0); }); loader.call(source, [], 1, 'EPSG:3857'); }); @@ -54,5 +58,23 @@ describe('ol.featureloader', function () { loader.call(source, [], 1, 'EPSG:3857'); }); }); + + it('it calls the success callback', function (done) { + const errorSpy = sinon.spy(); + loader = xhr(url, format); + loader.call( + source, + [], + 1, + 'EPSG:3857', + function () { + setTimeout(function () { + expect(errorSpy.callCount).to.be(0); + done(); + }, 0); + }, + errorSpy + ); + }); }); }); diff --git a/test/spec/ol/source/vector.test.js b/test/spec/ol/source/vector.test.js index 7f170adb41..7bf8df9aec 100644 --- a/test/spec/ol/source/vector.test.js +++ b/test/spec/ol/source/vector.test.js @@ -559,6 +559,35 @@ describe('ol.source.Vector', function () { }); describe('#loadFeatures', function () { + it('fires the FEATURESLOADSTART event', function (done) { + const source = new VectorSource(); + source.on('featuresloadstart', function () { + done(); + }); + source.loadFeatures( + [-10000, -10000, 10000, 10000], + 1, + getProjection('EPSG:3857') + ); + }); + + it('fires the FEATURESLOADEND event if the default load function is used', function (done) { + const source = new VectorSource({ + format: new GeoJSON(), + url: 'spec/ol/source/vectorsource/single-feature.json', + }); + source.on('featuresloadend', function (event) { + expect(event.features).to.be.an('array'); + expect(event.features.length).to.be(1); + done(); + }); + source.loadFeatures( + [-10000, -10000, 10000, 10000], + 1, + getProjection('EPSG:3857') + ); + }); + describe('with the "bbox" strategy', function () { it('requests the view extent plus render buffer', function (done) { const center = [-97.6114, 38.8403]; @@ -661,6 +690,54 @@ describe('ol.source.Vector', function () { getProjection('EPSG:3857') ); }); + + it('fires the FEATURESLOADEND event if the load function uses the callback', function (done) { + const source = new VectorSource(); + const spy = sinon.spy(); + source.on('featuresloadend', spy); + + const features = [new Feature(), new Feature()]; + + source.setLoader(function (bbox, resolution, projection, success) { + success(features); + setTimeout(function () { + expect(spy.calledOnce).to.be(true); + const event = spy.getCall(0).args[0]; + expect(event.features).to.be(features); + done(); + }, 0); + }); + source.loadFeatures( + [-10000, -10000, 10000, 10000], + 1, + getProjection('EPSG:3857') + ); + }); + + it('fires the FEATURESLOADERROR event if the load function uses the callback', function (done) { + const source = new VectorSource(); + const spy = sinon.spy(); + source.on('featuresloaderror', spy); + + source.setLoader(function ( + bbox, + resolution, + projection, + success, + failure + ) { + failure(); + setTimeout(function () { + expect(spy.calledOnce).to.be(true); + done(); + }, 0); + }); + source.loadFeatures( + [-10000, -10000, 10000, 10000], + 1, + getProjection('EPSG:3857') + ); + }); }); });