Allow data tile source loader to return a value or a promise
This commit is contained in:
@@ -32,7 +32,7 @@ const map = new Map({
|
|||||||
context.strokeRect(0, 0, size, size);
|
context.strokeRect(0, 0, size, size);
|
||||||
const data = context.getImageData(0, 0, size, size).data;
|
const data = context.getImageData(0, 0, size, size).data;
|
||||||
// converting to Uint8Array for increased browser compatibility
|
// converting to Uint8Array for increased browser compatibility
|
||||||
return Promise.resolve(new Uint8Array(data.buffer));
|
return new Uint8Array(data.buffer);
|
||||||
},
|
},
|
||||||
// disable opacity transition to avoid overlapping labels during tile loading
|
// disable opacity transition to avoid overlapping labels during tile loading
|
||||||
transition: 0,
|
transition: 0,
|
||||||
|
|||||||
@@ -58,3 +58,24 @@ export function memoizeOne(fn) {
|
|||||||
return lastResult;
|
return lastResult;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template T
|
||||||
|
* @param {function(): (T | Promise<T>)} getter A function that returns a value or a promise for a value.
|
||||||
|
* @return {Promise<T>} A promise for the value.
|
||||||
|
*/
|
||||||
|
export function toPromise(getter) {
|
||||||
|
function promiseGetter() {
|
||||||
|
let value;
|
||||||
|
try {
|
||||||
|
value = getter();
|
||||||
|
} catch (err) {
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
if (value instanceof Promise) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return Promise.resolve(value);
|
||||||
|
}
|
||||||
|
return promiseGetter();
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,11 +10,18 @@ import {assign} from '../obj.js';
|
|||||||
import {createXYZ, extentFromProjection} from '../tilegrid.js';
|
import {createXYZ, extentFromProjection} from '../tilegrid.js';
|
||||||
import {getKeyZXY} from '../tilecoord.js';
|
import {getKeyZXY} from '../tilecoord.js';
|
||||||
import {getUid} from '../util.js';
|
import {getUid} from '../util.js';
|
||||||
|
import {toPromise} from '../functions.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data tile loading function. The function is called with z, x, and y tile coordinates and
|
||||||
|
* returns {@link import("../DataTile.js").Data data} for a tile or a promise for the same.
|
||||||
|
* @typedef {function(number, number, number) : (import("../DataTile.js").Data|Promise<import("../DataTile.js").Data>)} Loader
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Options
|
* @typedef {Object} Options
|
||||||
* @property {function(number, number, number) : Promise<import("../DataTile.js").Data>} [loader] Data loader. Called with z, x, and y tile coordinates.
|
* @property {Loader} [loader] Data loader. Called with z, x, and y tile coordinates.
|
||||||
* Returns a promise that resolves to a {@link import("../DataTile.js").Data}.
|
* Returns {@link import("../DataTile.js").Data data} for a tile or a promise for the same.
|
||||||
* @property {number} [maxZoom=42] Optional max zoom level. Not used if `tileGrid` is provided.
|
* @property {number} [maxZoom=42] Optional max zoom level. Not used if `tileGrid` is provided.
|
||||||
* @property {number} [minZoom=0] Optional min zoom level. Not used if `tileGrid` is provided.
|
* @property {number} [minZoom=0] Optional min zoom level. Not used if `tileGrid` is provided.
|
||||||
* @property {number|import("../size.js").Size} [tileSize=[256, 256]] The pixel width and height of the tiles.
|
* @property {number|import("../size.js").Size} [tileSize=[256, 256]] The pixel width and height of the tiles.
|
||||||
@@ -31,7 +38,7 @@ import {getUid} from '../util.js';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
* Base class for sources providing tiles divided into a tile grid.
|
* A source for typed array data tiles.
|
||||||
*
|
*
|
||||||
* @fires import("./Tile.js").TileSourceEvent
|
* @fires import("./Tile.js").TileSourceEvent
|
||||||
* @api
|
* @api
|
||||||
@@ -86,7 +93,7 @@ class DataTileSource extends TileSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {function(number, number, number) : Promise<import("../DataTile.js").Data>} loader The data loader.
|
* @param {Loader} loader The data loader.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
setLoader(loader) {
|
setLoader(loader) {
|
||||||
@@ -109,8 +116,11 @@ class DataTileSource extends TileSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sourceLoader = this.loader_;
|
const sourceLoader = this.loader_;
|
||||||
|
|
||||||
function loader() {
|
function loader() {
|
||||||
return sourceLoader(z, x, y);
|
return toPromise(function () {
|
||||||
|
return sourceLoader(z, x, y);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const tile = new DataTile(
|
const tile = new DataTile(
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ describe('ol/renderer/webgl/Layer', function () {
|
|||||||
className: className,
|
className: className,
|
||||||
source: new DataTileSource({
|
source: new DataTileSource({
|
||||||
loader(z, x, y) {
|
loader(z, x, y) {
|
||||||
return Promise.resolve(new ImageData(256, 256));
|
return new ImageData(256, 256);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ describe('ol/renderer/webgl/TileLayer', function () {
|
|||||||
context.fillStyle = 'rgba(100, 100, 100, 0.5)';
|
context.fillStyle = 'rgba(100, 100, 100, 0.5)';
|
||||||
context.fillRect(0, 0, size, size);
|
context.fillRect(0, 0, size, size);
|
||||||
const data = context.getImageData(0, 0, size, size).data;
|
const data = context.getImageData(0, 0, size, size).data;
|
||||||
return Promise.resolve(data);
|
return data;
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,63 @@
|
|||||||
import expect from '../expect.js';
|
import expect from '../expect.js';
|
||||||
import {memoizeOne} from '../../../src/ol/functions.js';
|
import {memoizeOne, toPromise} from '../../../src/ol/functions.js';
|
||||||
|
|
||||||
describe('ol/functions.js', function () {
|
describe('ol/functions.js', function () {
|
||||||
|
describe('toPromise()', () => {
|
||||||
|
it('returns a promise given a getter for a value', (done) => {
|
||||||
|
const getter = () => 'a value';
|
||||||
|
const promise = toPromise(getter);
|
||||||
|
expect(promise).to.be.a(Promise);
|
||||||
|
promise.then((value) => {
|
||||||
|
expect(value).to.be('a value');
|
||||||
|
done();
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a promise given a getter for a promise that resolves', (done) => {
|
||||||
|
const getter = () => Promise.resolve('a value');
|
||||||
|
const promise = toPromise(getter);
|
||||||
|
expect(promise).to.be.a(Promise);
|
||||||
|
promise.then((value) => {
|
||||||
|
expect(value).to.be('a value');
|
||||||
|
done();
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a promise that rejects given a getter that throws', (done) => {
|
||||||
|
const getter = () => {
|
||||||
|
throw new Error('an error');
|
||||||
|
};
|
||||||
|
const promise = toPromise(getter);
|
||||||
|
expect(promise).to.be.a(Promise);
|
||||||
|
promise.then(
|
||||||
|
(value) => {
|
||||||
|
done(new Error(`expected promise to reject, got ${value}`));
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
expect(err).to.be.an(Error);
|
||||||
|
expect(err.message).to.be('an error');
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns a promise that rejects given a getter for a promse that rejects', (done) => {
|
||||||
|
const getter = () => Promise.reject(new Error('an error'));
|
||||||
|
const promise = toPromise(getter);
|
||||||
|
expect(promise).to.be.a(Promise);
|
||||||
|
promise.then(
|
||||||
|
(value) => {
|
||||||
|
done(new Error(`expected promise to reject, got ${value}`));
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
expect(err).to.be.an(Error);
|
||||||
|
expect(err.message).to.be('an error');
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('memoizeOne()', function () {
|
describe('memoizeOne()', function () {
|
||||||
it('returns the result from the first call when called a second time with the same args', function () {
|
it('returns the result from the first call when called a second time with the same args', function () {
|
||||||
const arg1 = {};
|
const arg1 = {};
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ new Map({
|
|||||||
|
|
||||||
const bandCount = 3;
|
const bandCount = 3;
|
||||||
const result = data.filter((_, index) => index % 4 < bandCount);
|
const result = data.filter((_, index) => index % 4 < bandCount);
|
||||||
return Promise.resolve(result);
|
return result;
|
||||||
},
|
},
|
||||||
tileSize: size,
|
tileSize: size,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ new Map({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(output);
|
return output;
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ new Map({
|
|||||||
labelCanvasSize,
|
labelCanvasSize,
|
||||||
labelCanvasSize
|
labelCanvasSize
|
||||||
).data;
|
).data;
|
||||||
return Promise.resolve(new Uint8Array(data.buffer));
|
return new Uint8Array(data.buffer);
|
||||||
},
|
},
|
||||||
transition: 0,
|
transition: 0,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ new Map({
|
|||||||
labelCanvasSize,
|
labelCanvasSize,
|
||||||
labelCanvasSize
|
labelCanvasSize
|
||||||
).data;
|
).data;
|
||||||
return Promise.resolve(new Uint8Array(data.buffer));
|
return new Uint8Array(data.buffer);
|
||||||
},
|
},
|
||||||
transition: 0,
|
transition: 0,
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user