Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VectorTileWorkerSource: fix reload for original's load parse would not pass the rawTileData and meta #2941

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

### 🐞 Bug fixes

- VectorTileWorkerSource: fix reload for original's load parse would not pass the rawTileData and meta. ([#2941](https://github.com/maplibre/maplibre-gl-js/pull/2941))
- _...Add new stuff here..._

## 3.2.1
Expand Down
63 changes: 46 additions & 17 deletions src/source/vector_tile_worker_source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {VectorTileWorkerSource} from '../source/vector_tile_worker_source';
import {StyleLayerIndex} from '../style/style_layer_index';
import {fakeServer, FakeServer} from 'nise';
import {Actor} from '../util/actor';
import {TileParameters, WorkerTileParameters, WorkerTileResult} from './worker_source';
import {TileParameters, WorkerTileParameters} from './worker_source';
import {WorkerTile} from './worker_tile';
import {setPerformance} from '../util/test/util';

Expand Down Expand Up @@ -90,7 +90,27 @@ describe('vector tile worker source', () => {
const rawTileData = new Uint8Array([]);
function loadVectorData(params, callback) {
return callback(null, {
vectorTile: new vt.VectorTile(new Protobuf(rawTileData)),
vectorTile: {
layers: {
test: {
version: 2,
name: 'test',
extent: 8192,
length: 1,
feature: (featureIndex: number) => ({
extent: 8192,
type: 1,
id: featureIndex,
properties: {
name: 'test'
},
loadGeometry () {
return [[{x: 0, y: 0}]];
}
})
}
}
} as any as vt.VectorTile,
rawData: rawTileData
});
}
Expand All @@ -99,28 +119,37 @@ describe('vector tile worker source', () => {
id: 'test',
source: 'source',
'source-layer': 'test',
type: 'fill'
type: 'symbol',
layout: {
'icon-image': 'hello',
'text-font': ['StandardFont-Bold'],
'text-field': '{name}'
}
}]);

const source = new VectorTileWorkerSource(actor, layerIndex, [], loadVectorData);
const send = jest.fn().mockImplementation((type: string, data: unknown, callback: Function) => {
const res = setTimeout(() => callback(null,
type === 'getImages' ?
{'hello': {width: 1, height: 1, data: new Uint8Array([0])}} :
{'StandardFont-Bold': {width: 1, height: 1, data: new Uint8Array([0])}}
));

const parseWorkerTileMock = jest
.spyOn(WorkerTile.prototype, 'parse')
.mockImplementation(function(data, layerIndex, availableImages, actor, callback) {
this.status = 'parsing';
window.setTimeout(() => callback(null, {} as WorkerTileResult), 10);
});
return {
cancel: () => clearTimeout(res)
};
});

let loadCallbackCalled = false;
const actor = {
send
} as unknown as Actor;
const source = new VectorTileWorkerSource(actor, layerIndex, ['hello'], loadVectorData);
source.loadTile({
source: 'source',
uid: 0,
tileID: {overscaledZ: 0, wrap: 0, canonical: {x: 0, y: 0, z: 0, w: 0}},
request: {url: 'http://localhost:2900/faketile.pbf'}
} as any as WorkerTileParameters, (err, res) => {
expect(err).toBeFalsy();
expect(res).toBeDefined();
loadCallbackCalled = true;
} as any as WorkerTileParameters, () => {
done.fail('should not be called');
});

source.reloadTile({
Expand All @@ -130,8 +159,8 @@ describe('vector tile worker source', () => {
} as any as WorkerTileParameters, (err, res) => {
expect(err).toBeFalsy();
expect(res).toBeDefined();
expect(parseWorkerTileMock).toHaveBeenCalledTimes(2);
expect(loadCallbackCalled).toBeTruthy();
expect(res.rawTileData).toBeDefined();
expect(res.rawTileData).toStrictEqual(rawTileData);
done();
});
});
Expand Down
27 changes: 26 additions & 1 deletion src/source/vector_tile_worker_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ function loadVectorTile(params: WorkerTileParameters, callback: LoadVectorDataCa
};
}

interface FetchingState {
ambientlight marked this conversation as resolved.
Show resolved Hide resolved
rawTileData: ArrayBuffer;
cacheControl: ExpiryData;
resourceTiming: any;
}

/**
* The {@link WorkerSource} implementation that supports {@link VectorTileSource}.
* This class is designed to be easily reused to support custom source types
Expand All @@ -66,6 +72,7 @@ export class VectorTileWorkerSource implements WorkerSource {
layerIndex: StyleLayerIndex;
availableImages: Array<string>;
loadVectorData: LoadVectorData;
fetching: {[_: string]: FetchingState };
loading: {[_: string]: WorkerTile};
loaded: {[_: string]: WorkerTile};

Expand All @@ -80,6 +87,7 @@ export class VectorTileWorkerSource implements WorkerSource {
this.layerIndex = layerIndex;
this.availableImages = availableImages;
this.loadVectorData = loadVectorData || loadVectorTile;
this.fetching = {};
this.loading = {};
this.loaded = {};
}
Expand Down Expand Up @@ -124,6 +132,7 @@ export class VectorTileWorkerSource implements WorkerSource {

workerTile.vectorTile = response.vectorTile;
workerTile.parse(response.vectorTile, this.layerIndex, this.availableImages, this.actor, (err, result) => {
delete this.fetching[uid];
if (err || !result) return callback(err);

// Transferring a copy of rawTileData because the worker needs to retain its copy.
Expand All @@ -132,6 +141,8 @@ export class VectorTileWorkerSource implements WorkerSource {

this.loaded = this.loaded || {};
this.loaded[uid] = workerTile;
// keep the original fetching state so that reload tile can pick it up if the original parse is cancelled by reloads' parse
this.fetching[uid] = {rawTileData, cacheControl, resourceTiming};
}) as AbortVectorData;
}

Expand All @@ -145,7 +156,21 @@ export class VectorTileWorkerSource implements WorkerSource {
const workerTile = loaded[uid];
workerTile.showCollisionBoxes = params.showCollisionBoxes;
if (workerTile.status === 'parsing') {
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, callback);
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.availableImages, this.actor, (err, result) => {
if (err || !result) return callback(err, result);

// if we have cancelled the original parse, make sure to pass the rawTileData from the original fetch
let parseResult;
if (this.fetching[uid]) {
const {rawTileData, cacheControl, resourceTiming} = this.fetching[uid];
delete this.fetching[uid];
parseResult = extend({rawTileData: rawTileData.slice(0)}, result, cacheControl, resourceTiming);
} else {
parseResult = result;
}

callback(null, parseResult);
});
} else if (workerTile.status === 'done') {
// if there was no vector tile data on the initial load, don't try and re-parse tile
if (workerTile.vectorTile) {
Expand Down