mapbox-gl
Version:
A WebGL interactive maps library
163 lines (136 loc) • 5.87 kB
JavaScript
// @flow
const Actor = require('../util/actor');
const StyleLayerIndex = require('../style/style_layer_index');
const VectorTileWorkerSource = require('./vector_tile_worker_source');
const GeoJSONWorkerSource = require('./geojson_worker_source');
const assert = require('assert');
const globalRTLTextPlugin = require('./rtl_text_plugin');
import type {
WorkerSource,
WorkerTileParameters,
WorkerTileCallback,
TileParameters,
RedoPlacementParameters,
RedoPlacementCallback
} from '../source/worker_source';
/**
* @private
*/
class Worker {
self: WorkerGlobalScope & {
registerWorkerSource: (string, Class<WorkerSource>) => void,
registerRTLTextPlugin: (any) => void
};
actor: Actor;
layerIndexes: { [string]: StyleLayerIndex };
workerSourceTypes: { [string]: Class<WorkerSource> };
workerSources: { [string]: { [string]: WorkerSource } };
constructor(self: WorkerGlobalScope) {
this.self = (self: any); // Needs a cast because we're going to extend it with `register*` methods.
this.actor = new Actor(self, this);
this.layerIndexes = {};
this.workerSourceTypes = {
vector: VectorTileWorkerSource,
geojson: GeoJSONWorkerSource
};
// [mapId][sourceType] => worker source instance
this.workerSources = {};
this.self.registerWorkerSource = (name, WorkerSource) => {
if (this.workerSourceTypes[name]) {
throw new Error(`Worker source with name "${name}" already registered.`);
}
this.workerSourceTypes[name] = WorkerSource;
};
this.self.registerRTLTextPlugin = (rtlTextPlugin) => {
if (globalRTLTextPlugin.applyArabicShaping || globalRTLTextPlugin.processBidirectionalText) {
throw new Error('RTL text plugin already registered.');
}
globalRTLTextPlugin['applyArabicShaping'] = rtlTextPlugin.applyArabicShaping;
globalRTLTextPlugin['processBidirectionalText'] = rtlTextPlugin.processBidirectionalText;
};
}
setLayers(mapId: string, layers: Array<LayerSpecification>) {
this.getLayerIndex(mapId).replace(layers);
}
updateLayers(mapId: string, params: {layers: Array<LayerSpecification>, removedIds: Array<string>, symbolOrder: ?Array<string>}) {
this.getLayerIndex(mapId).update(params.layers, params.removedIds, params.symbolOrder);
}
loadTile(mapId: string, params: WorkerTileParameters & {type: string}, callback: WorkerTileCallback) {
assert(params.type);
this.getWorkerSource(mapId, params.type).loadTile(params, callback);
}
reloadTile(mapId: string, params: WorkerTileParameters & {type: string}, callback: WorkerTileCallback) {
assert(params.type);
this.getWorkerSource(mapId, params.type).reloadTile(params, callback);
}
abortTile(mapId: string, params: TileParameters & {type: string}) {
assert(params.type);
this.getWorkerSource(mapId, params.type).abortTile(params);
}
removeTile(mapId: string, params: TileParameters & {type: string}) {
assert(params.type);
this.getWorkerSource(mapId, params.type).removeTile(params);
}
removeSource(mapId: string, params: {source: string} & {type: string}) {
assert(params.type);
const worker = this.getWorkerSource(mapId, params.type);
if (worker.removeSource !== undefined) {
worker.removeSource(params);
}
}
redoPlacement(mapId: string, params: RedoPlacementParameters & {type: string}, callback: RedoPlacementCallback) {
assert(params.type);
this.getWorkerSource(mapId, params.type).redoPlacement(params, callback);
}
/**
* Load a {@link WorkerSource} script at params.url. The script is run
* (using importScripts) with `registerWorkerSource` in scope, which is a
* function taking `(name, workerSourceObject)`.
* @private
*/
loadWorkerSource(map: string, params: {url: string}, callback: Callback<void>) {
try {
this.self.importScripts(params.url);
callback();
} catch (e) {
callback(e);
}
}
loadRTLTextPlugin(map: string, pluginURL: string, callback: Callback<void>) {
try {
if (!globalRTLTextPlugin.applyArabicShaping && !globalRTLTextPlugin.processBidirectionalText) {
this.self.importScripts(pluginURL);
if (!globalRTLTextPlugin.applyArabicShaping || !globalRTLTextPlugin.processBidirectionalText) {
callback(new Error(`RTL Text Plugin failed to import scripts from ${pluginURL}`));
}
}
} catch (e) {
callback(e);
}
}
getLayerIndex(mapId: string) {
let layerIndexes = this.layerIndexes[mapId];
if (!layerIndexes) {
layerIndexes = this.layerIndexes[mapId] = new StyleLayerIndex();
}
return layerIndexes;
}
getWorkerSource(mapId: string, type: string) {
if (!this.workerSources[mapId])
this.workerSources[mapId] = {};
if (!this.workerSources[mapId][type]) {
// use a wrapped actor so that we can attach a target mapId param
// to any messages invoked by the WorkerSource
const actor = {
send: (type, data, callback, buffers) => {
this.actor.send(type, data, callback, buffers, mapId);
}
};
this.workerSources[mapId][type] = new this.workerSourceTypes[type](actor, this.getLayerIndex(mapId));
}
return this.workerSources[mapId][type];
}
}
module.exports = function createWorker(self: WorkerGlobalScope) {
return new Worker(self);
};