Skip to content

Commit

Permalink
Async actor docs additions (#3305)
Browse files Browse the repository at this point in the history
* Fix missing classes in the docs

* Fixed the relevant docs in the places where API calls were changed.

* Fix lint

* Remove some "then" from tests

* remove todos, remove "then"s, simplify the ajax code a bit

* Remove as any from where it's not needed

* Fix code review expect stuff...

* explicit promises instead of array.

* Improve ajax further
  • Loading branch information
HarelM authored Nov 7, 2023
1 parent 837695c commit 5e4cdde
Show file tree
Hide file tree
Showing 27 changed files with 412 additions and 475 deletions.
35 changes: 24 additions & 11 deletions src/data/dem_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ import {RGBAImage} from '../util/image';
import {warnOnce} from '../util/util';
import {register} from '../util/web_worker_transfer';

// DEMData is a data structure for decoding, backfilling, and storing elevation data for processing in the hillshade shaders
// data can be populated either from a pngraw image tile or from serliazed data sent back from a worker. When data is initially
// loaded from a image tile, we decode the pixel values using the appropriate decoding formula, but we store the
// elevation data as an Int32 value. we add 65536 (2^16) to eliminate negative values and enable the use of
// integer overflow when creating the texture used in the hillshadePrepare step.

// DEMData also handles the backfilling of data from a tile's neighboring tiles. This is necessary because we use a pixel's 8
// surrounding pixel values to compute the slope at that pixel, and we cannot accurately calculate the slope at pixels on a
// tile's edge without backfilling from neighboring tiles.

/**
* The possible DEM encoding types
*/
export type DEMEncoding = 'mapbox' | 'terrarium' | 'custom'

/**
* DEMData is a data structure for decoding, backfilling, and storing elevation data for processing in the hillshade shaders
* data can be populated either from a pngraw image tile or from serliazed data sent back from a worker. When data is initially
* loaded from a image tile, we decode the pixel values using the appropriate decoding formula, but we store the
* elevation data as an Int32 value. we add 65536 (2^16) to eliminate negative values and enable the use of
* integer overflow when creating the texture used in the hillshadePrepare step.
*
* DEMData also handles the backfilling of data from a tile's neighboring tiles. This is necessary because we use a pixel's 8
* surrounding pixel values to compute the slope at that pixel, and we cannot accurately calculate the slope at pixels on a
* tile's edge without backfilling from neighboring tiles.
*/
export class DEMData {
uid: string | number;
data: Uint32Array;
Expand All @@ -27,8 +31,17 @@ export class DEMData {
blueFactor: number;
baseShift: number;

// RGBAImage data has uniform 1px padding on all sides: square tile edge size defines stride
/**
* Constructs a `DEMData` object
* @param uid - the tile's unique id
* @param data - RGBAImage data has uniform 1px padding on all sides: square tile edge size defines stride
// and dim is calculated as stride - 2.
* @param encoding - the encoding type of the data
* @param redFactor - the red channel factor used to unpack the data, used for `custom` encoding only
* @param greenFactor - the green channel factor used to unpack the data, used for `custom` encoding only
* @param blueFactor - the blue channel factor used to unpack the data, used for `custom` encoding only
* @param baseShift - the base shift used to unpack the data, used for `custom` encoding only
*/
constructor(uid: string | number, data: RGBAImage | ImageData, encoding: DEMEncoding, redFactor = 1.0, greenFactor = 1.0, blueFactor = 1.0, baseShift = 0.0) {
this.uid = uid;
if (data.height !== data.width) throw new RangeError('DEM tiles must be square');
Expand Down
1 change: 0 additions & 1 deletion src/data/feature_index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ type QueryParameters = {
};

/**
* @internal
* An in memory index class to allow fast interaction with features
*/
export class FeatureIndex {
Expand Down
150 changes: 65 additions & 85 deletions src/render/glyph_manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,148 +3,128 @@ import {GlyphManager} from './glyph_manager';
import fs from 'fs';
import {RequestManager} from '../util/request_manager';

const glyphs = {};
for (const glyph of parseGlyphPbf(fs.readFileSync('./test/unit/assets/0-255.pbf'))) {
glyphs[glyph.id] = glyph;
}

const identityTransform = ((url) => ({url})) as any as RequestManager;

const createLoadGlyphRangeStub = () => {
return jest.spyOn(GlyphManager, 'loadGlyphRange').mockImplementation((stack, range, urlTemplate, transform) => {
expect(stack).toBe('Arial Unicode MS');
expect(range).toBe(0);
expect(urlTemplate).toBe('https://localhost/fonts/v1/{fontstack}/{range}.pbf');
expect(transform).toBe(identityTransform);
return Promise.resolve(glyphs);
});
};

const createGlyphManager = (font?) => {
const manager = new GlyphManager(identityTransform, font);
manager.setURL('https://localhost/fonts/v1/{fontstack}/{range}.pbf');
return manager;
};
describe('GlyphManager', () => {
const GLYPHS = {};
for (const glyph of parseGlyphPbf(fs.readFileSync('./test/unit/assets/0-255.pbf'))) {
GLYPHS[glyph.id] = glyph;
}

const identityTransform = ((url) => ({url})) as any as RequestManager;

const createLoadGlyphRangeStub = () => {
return jest.spyOn(GlyphManager, 'loadGlyphRange').mockImplementation((stack, range, urlTemplate, transform) => {
expect(stack).toBe('Arial Unicode MS');
expect(range).toBe(0);
expect(urlTemplate).toBe('https://localhost/fonts/v1/{fontstack}/{range}.pbf');
expect(transform).toBe(identityTransform);
return Promise.resolve(GLYPHS);
});
};

afterEach(() => {
jest.clearAllMocks();
});
const createGlyphManager = (font?) => {
const manager = new GlyphManager(identityTransform, font);
manager.setURL('https://localhost/fonts/v1/{fontstack}/{range}.pbf');
return manager;
};

describe('GlyphManager', () => {
afterEach(() => {
jest.clearAllMocks();
});

test('GlyphManager requests 0-255 PBF', done => {
test('GlyphManager requests 0-255 PBF', async () => {
createLoadGlyphRangeStub();
const manager = createGlyphManager();

manager.getGlyphs({'Arial Unicode MS': [55]}).then((glyphs) => {
expect(glyphs['Arial Unicode MS']['55'].metrics.advance).toBe(12);
done();
});
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [55]});
expect(returnedGlyphs['Arial Unicode MS']['55'].metrics.advance).toBe(12);
});

test('GlyphManager doesn\'t request twice 0-255 PBF if a glyph is missing', done => {
test('GlyphManager doesn\'t request twice 0-255 PBF if a glyph is missing', async () => {
const stub = createLoadGlyphRangeStub();
const manager = createGlyphManager();

manager.getGlyphs({'Arial Unicode MS': [0.5]}).then(() => {
expect(manager.entries['Arial Unicode MS'].ranges[0]).toBe(true);
expect(stub).toHaveBeenCalledTimes(1);
await manager.getGlyphs({'Arial Unicode MS': [0.5]});
expect(manager.entries['Arial Unicode MS'].ranges[0]).toBe(true);
expect(stub).toHaveBeenCalledTimes(1);

// We remove all requests as in getGlyphs code.
delete manager.entries['Arial Unicode MS'].requests[0];
// We remove all requests as in getGlyphs code.
delete manager.entries['Arial Unicode MS'].requests[0];

manager.getGlyphs({'Arial Unicode MS': [0.5]}).then(() => {
expect(manager.entries['Arial Unicode MS'].ranges[0]).toBe(true);
expect(stub).toHaveBeenCalledTimes(1);
done();
});
});
await manager.getGlyphs({'Arial Unicode MS': [0.5]});
expect(manager.entries['Arial Unicode MS'].ranges[0]).toBe(true);
expect(stub).toHaveBeenCalledTimes(1);
});

test('GlyphManager requests remote CJK PBF', done => {
test('GlyphManager requests remote CJK PBF', async () => {
jest.spyOn(GlyphManager, 'loadGlyphRange').mockImplementation((_stack, _range, _urlTemplate, _transform) => {
return Promise.resolve(glyphs);
return Promise.resolve(GLYPHS);
});

const manager = createGlyphManager();

manager.getGlyphs({'Arial Unicode MS': [0x5e73]}).then((glyphs) => {
expect(glyphs['Arial Unicode MS'][0x5e73]).toBeNull(); // The fixture returns a PBF without the glyph we requested
done();
});
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x5e73]});
expect(returnedGlyphs['Arial Unicode MS'][0x5e73]).toBeNull(); // The fixture returns a PBF without the glyph we requested
});

test('GlyphManager does not cache CJK chars that should be rendered locally', done => {
test('GlyphManager does not cache CJK chars that should be rendered locally', async () => {
jest.spyOn(GlyphManager, 'loadGlyphRange').mockImplementation((_stack, range, _urlTemplate, _transform) => {
const overlappingGlyphs = {};
const start = range * 256;
const end = start + 256;
for (let i = start, j = 0; i < end; i++, j++) {
overlappingGlyphs[i] = glyphs[j];
overlappingGlyphs[i] = GLYPHS[j];
}
return Promise.resolve(overlappingGlyphs);
});

const manager = createGlyphManager('sans-serif');

//Request char that overlaps Katakana range
manager.getGlyphs({'Arial Unicode MS': [0x3005]}).then((glyphs) => {
expect(glyphs['Arial Unicode MS'][0x3005]).not.toBeNull();
//Request char from Katakana range (te テ)
manager.getGlyphs({'Arial Unicode MS': [0x30C6]}).then((glyphs) => {
const glyph = glyphs['Arial Unicode MS'][0x30c6];
//Ensure that te is locally generated.
expect(glyph.bitmap.height).toBe(12);
expect(glyph.bitmap.width).toBe(12);
done();
});
});
let returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x3005]});
expect(returnedGlyphs['Arial Unicode MS'][0x3005]).not.toBeNull();
//Request char from Katakana range (te テ)
returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x30C6]});
const glyph = returnedGlyphs['Arial Unicode MS'][0x30c6];
//Ensure that te is locally generated.
expect(glyph.bitmap.height).toBe(12);
expect(glyph.bitmap.width).toBe(12);
});

test('GlyphManager generates CJK PBF locally', done => {
test('GlyphManager generates CJK PBF locally', async () => {
const manager = createGlyphManager('sans-serif');

// character 平
manager.getGlyphs({'Arial Unicode MS': [0x5e73]}).then((glyphs) => {
expect(glyphs['Arial Unicode MS'][0x5e73].metrics.advance).toBe(0.5);
done();
});
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x5e73]});
expect(returnedGlyphs['Arial Unicode MS'][0x5e73].metrics.advance).toBe(0.5);
});

test('GlyphManager generates Katakana PBF locally', done => {
test('GlyphManager generates Katakana PBF locally', async () => {
const manager = createGlyphManager('sans-serif');

// Katakana letter te テ
manager.getGlyphs({'Arial Unicode MS': [0x30c6]}).then((glyphs) => {
expect(glyphs['Arial Unicode MS'][0x30c6].metrics.advance).toBe(0.5);
done();
});
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x30c6]});
expect(returnedGlyphs['Arial Unicode MS'][0x30c6].metrics.advance).toBe(0.5);
});

test('GlyphManager generates Hiragana PBF locally', done => {
test('GlyphManager generates Hiragana PBF locally', async () => {
const manager = createGlyphManager('sans-serif');

//Hiragana letter te て
manager.getGlyphs({'Arial Unicode MS': [0x3066]}).then((glyphs) => {
expect(glyphs['Arial Unicode MS'][0x3066].metrics.advance).toBe(0.5);
done();
});
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x3066]});
expect(returnedGlyphs['Arial Unicode MS'][0x3066].metrics.advance).toBe(0.5);
});

test('GlyphManager caches locally generated glyphs', done => {
test('GlyphManager caches locally generated glyphs', async () => {

const manager = createGlyphManager('sans-serif');
const drawSpy = GlyphManager.TinySDF.prototype.draw = jest.fn().mockImplementation(() => {
return {data: new Uint8ClampedArray(60 * 60)} as any;
});

// Katakana letter te
manager.getGlyphs({'Arial Unicode MS': [0x30c6]}).then((glyphs) => {
expect(glyphs['Arial Unicode MS'][0x30c6].metrics.advance).toBe(24);
manager.getGlyphs({'Arial Unicode MS': [0x30c6]}).then(() => {
expect(drawSpy).toHaveBeenCalledTimes(1);
done();
});
});
const returnedGlyphs = await manager.getGlyphs({'Arial Unicode MS': [0x30c6]});
expect(returnedGlyphs['Arial Unicode MS'][0x30c6].metrics.advance).toBe(24);
await manager.getGlyphs({'Arial Unicode MS': [0x30c6]});
expect(drawSpy).toHaveBeenCalledTimes(1);
});
});
1 change: 0 additions & 1 deletion src/render/image_atlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ export class ImagePosition {
}

/**
* @internal
* A class holding all the images
*/
export class ImageAtlas {
Expand Down
Loading

0 comments on commit 5e4cdde

Please sign in to comment.