mirror of https://gitee.com/antv-l7/antv-l7
feat(worker): worker Source
This commit is contained in:
parent
bab462ac40
commit
3bbdb5ec88
|
@ -29,7 +29,7 @@ const scene = new L7.Scene({
|
||||||
});
|
});
|
||||||
window.scene = scene;
|
window.scene = scene;
|
||||||
scene.on('loaded', () => {
|
scene.on('loaded', () => {
|
||||||
$.get('./data/provincePoint.json', data => {
|
$.get('https://gw.alipayobjects.com/os/basement_prod/abcfe339-b8bc-46ce-8ff4-c96185b6235f.json', data => {
|
||||||
scene.PointLayer({
|
scene.PointLayer({
|
||||||
zIndex: 2
|
zIndex: 2
|
||||||
})
|
})
|
||||||
|
|
|
@ -51,7 +51,7 @@ scene.on('loaded', () => {
|
||||||
})
|
})
|
||||||
//.animate({enable:true})
|
//.animate({enable:true})
|
||||||
.render();
|
.render();
|
||||||
console.log(layer);
|
|
||||||
/**
|
/**
|
||||||
scene.LineLayer({
|
scene.LineLayer({
|
||||||
zIndex: 2
|
zIndex: 2
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<meta name="geometry" content="diagram">
|
||||||
|
<link rel="stylesheet" href="./assets/common.css">
|
||||||
|
<link rel="stylesheet" href="./assets/info.css">
|
||||||
|
|
||||||
|
<title>hexagon demo</title>
|
||||||
|
<style>
|
||||||
|
body {margin: 0;}
|
||||||
|
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="map"></div>
|
||||||
|
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||||
|
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||||
|
<script src="./assets/dat.gui.min.js"></script>
|
||||||
|
<script src="../build/L7.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const scene = new L7.Scene({
|
||||||
|
id: 'map',
|
||||||
|
mapStyle: 'light', // 样式URL
|
||||||
|
center: [104.838088,34.075889 ],
|
||||||
|
pitch: 0,
|
||||||
|
hash:true,
|
||||||
|
zoom: 3,
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
scene.on('loaded', () => {
|
||||||
|
scene.addTileSource('test',{
|
||||||
|
url:'http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/county/{z}/{x}/{y}.pbf',
|
||||||
|
type:'mvt',
|
||||||
|
minZoom: 0,
|
||||||
|
maxZoom:9
|
||||||
|
})
|
||||||
|
const layer = scene.PolygonLayer({
|
||||||
|
zIndex:0,
|
||||||
|
})
|
||||||
|
.source('test',{
|
||||||
|
parser:{
|
||||||
|
type: 'mvt',
|
||||||
|
sourceLayer:'county',
|
||||||
|
idField:'id'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.shape('line')
|
||||||
|
.size(2)
|
||||||
|
.active(false)
|
||||||
|
.color('red')
|
||||||
|
.style({
|
||||||
|
opacity:1.0
|
||||||
|
})
|
||||||
|
.render();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -69,6 +69,7 @@ export default class Layer extends Base {
|
||||||
this._object3D.renderOrder = this.get('zIndex') || 0;
|
this._object3D.renderOrder = this.get('zIndex') || 0;
|
||||||
this._mapEventHandlers = [];
|
this._mapEventHandlers = [];
|
||||||
const layerId = this._getUniqueId();
|
const layerId = this._getUniqueId();
|
||||||
|
this.set('layerId', layerId);
|
||||||
this.layerId = layerId;
|
this.layerId = layerId;
|
||||||
this._activeIds = null;
|
this._activeIds = null;
|
||||||
const world = scene._engine.world;
|
const world = scene._engine.world;
|
||||||
|
@ -127,7 +128,19 @@ export default class Layer extends Base {
|
||||||
this.set('visible', visible);
|
this.set('visible', visible);
|
||||||
this._object3D.visible = this.get('visible');
|
this._object3D.visible = this.get('visible');
|
||||||
}
|
}
|
||||||
|
// 兼容瓦片source,非瓦片source
|
||||||
|
|
||||||
source(data, cfg = {}) {
|
source(data, cfg = {}) {
|
||||||
|
// 根据Source类型判断,是不是瓦片图层
|
||||||
|
if (this.scene.getTileSource(data)) {
|
||||||
|
this.set('layerType', 'tile');
|
||||||
|
this.set('sourceOption', {
|
||||||
|
id: data,
|
||||||
|
...cfg
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
if (data instanceof source) {
|
if (data instanceof source) {
|
||||||
this.layerSource = data;
|
this.layerSource = data;
|
||||||
return this;
|
return this;
|
||||||
|
@ -136,9 +149,6 @@ export default class Layer extends Base {
|
||||||
cfg.mapType = this.scene.mapType;
|
cfg.mapType = this.scene.mapType;
|
||||||
cfg.zoom = this.scene.getZoom();
|
cfg.zoom = this.scene.getZoom();
|
||||||
this.layerSource = new source(cfg);
|
this.layerSource = new source(cfg);
|
||||||
// this.scene.workerPool.runTask(cfg).then(data => {
|
|
||||||
// console.log(data);
|
|
||||||
// });
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
color(field, values) {
|
color(field, values) {
|
||||||
|
@ -305,6 +315,10 @@ export default class Layer extends Base {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (this.get('layerType') === 'tile') {
|
||||||
|
this.scene.style.update(this._attrs);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
this.init();
|
this.init();
|
||||||
this.scene._engine.update();
|
this.scene._engine.update();
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -3,12 +3,13 @@ import { LAYER_MAP } from '../layer';
|
||||||
import Base from './base';
|
import Base from './base';
|
||||||
import LoadImage from './image';
|
import LoadImage from './image';
|
||||||
import FontAtlasManager from '../geom/buffer/point/text/font-manager';
|
import FontAtlasManager from '../geom/buffer/point/text/font-manager';
|
||||||
import WorkerPool from '../worker/worker_pool';
|
|
||||||
// import { MapProvider } from '../map/AMap';
|
// import { MapProvider } from '../map/AMap';
|
||||||
import { getMap } from '../map/index';
|
import { getMap } from '../map/index';
|
||||||
import Global from '../global';
|
import Global from '../global';
|
||||||
import { getInteraction } from '../interaction/index';
|
import { getInteraction } from '../interaction/index';
|
||||||
import { compileBuiltinModules } from '../geom/shader';
|
import { compileBuiltinModules } from '../geom/shader';
|
||||||
|
import Style from './style';
|
||||||
|
import { epsg3857 } from '@antv/geo-coord/lib/geo/crs/crs-epsg3857';
|
||||||
export default class Scene extends Base {
|
export default class Scene extends Base {
|
||||||
getDefaultCfg() {
|
getDefaultCfg() {
|
||||||
return Global.scene;
|
return Global.scene;
|
||||||
|
@ -16,6 +17,7 @@ export default class Scene extends Base {
|
||||||
constructor(cfg) {
|
constructor(cfg) {
|
||||||
super(cfg);
|
super(cfg);
|
||||||
this._initMap();
|
this._initMap();
|
||||||
|
this.crs = epsg3857;
|
||||||
// this._initAttribution(); // 暂时取消,后面作为组件去加载
|
// this._initAttribution(); // 暂时取消,后面作为组件去加载
|
||||||
this.addImage();
|
this.addImage();
|
||||||
this.fontAtlasManager = new FontAtlasManager();
|
this.fontAtlasManager = new FontAtlasManager();
|
||||||
|
@ -27,7 +29,6 @@ export default class Scene extends Base {
|
||||||
this._engine = new Engine(mapContainer, this);
|
this._engine = new Engine(mapContainer, this);
|
||||||
// this.registerMapEvent();
|
// this.registerMapEvent();
|
||||||
this._engine.run();
|
this._engine.run();
|
||||||
this.workerPool = new WorkerPool();
|
|
||||||
compileBuiltinModules();
|
compileBuiltinModules();
|
||||||
}
|
}
|
||||||
// 为pickup场景添加 object 对象
|
// 为pickup场景添加 object 对象
|
||||||
|
@ -54,6 +55,7 @@ export default class Scene extends Base {
|
||||||
const interaction = new Ctor({ layer: this });
|
const interaction = new Ctor({ layer: this });
|
||||||
interaction._onHashChange();
|
interaction._onHashChange();
|
||||||
}
|
}
|
||||||
|
this.style = new Style(this, {});
|
||||||
this.emit('loaded');
|
this.emit('loaded');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -68,6 +70,13 @@ export default class Scene extends Base {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// 添加 Tile Source
|
||||||
|
addTileSource(id, Sourcecfg) {
|
||||||
|
this.style.addSource(id, Sourcecfg);
|
||||||
|
}
|
||||||
|
getTileSource(id) {
|
||||||
|
return this.style.getSource(id);
|
||||||
|
}
|
||||||
on(type, hander) {
|
on(type, hander) {
|
||||||
if (this.map) { this.map.on(type, hander); }
|
if (this.map) { this.map.on(type, hander); }
|
||||||
super.on(type, hander);
|
super.on(type, hander);
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
import Base from '../core/base';
|
||||||
|
import WorkerPool from '../worker/worker_pool';
|
||||||
|
import { toLngLat, Bounds } from '@antv/geo-coord';
|
||||||
|
import SourceCache from '../source/source_cache';
|
||||||
|
import WorkerController from '../worker/worker_controller';
|
||||||
|
// 统一管理所有的Source
|
||||||
|
// 统一管理地图样式
|
||||||
|
export default class Style extends Base {
|
||||||
|
constructor(scene, cfg) {
|
||||||
|
super(cfg);
|
||||||
|
this.scene = scene;
|
||||||
|
this._sourceCaches = {};
|
||||||
|
this.WorkerPool = new WorkerPool();
|
||||||
|
this._tileMap = {};
|
||||||
|
this.WorkerController = new WorkerController(this.WorkerPool, this);
|
||||||
|
this.layerStyles = {};
|
||||||
|
this.addMapEvent();
|
||||||
|
}
|
||||||
|
addSource(id, sourceCfg) {
|
||||||
|
if (this._sourceCaches[id] !== undefined) {
|
||||||
|
throw new Error('SourceID 已存在');
|
||||||
|
}
|
||||||
|
this._sourceCaches[id] = new SourceCache(this.scene, sourceCfg);
|
||||||
|
}
|
||||||
|
getSource(id) {
|
||||||
|
return this._sourceCaches[id];
|
||||||
|
}
|
||||||
|
// 设置
|
||||||
|
addTileStyle(layerCfg) {
|
||||||
|
const layerid = layerCfg.layerId;
|
||||||
|
this.layerStyles[layerid] = layerCfg;
|
||||||
|
this._layerStyleGroupBySourceID();
|
||||||
|
this.WorkerController.broadcast('setLayers', this.layerStyles);
|
||||||
|
// TODO 更新 style
|
||||||
|
|
||||||
|
}
|
||||||
|
removeTileStyle(id) {
|
||||||
|
delete this.layerStyles[id];
|
||||||
|
this._layerStyleGroupBySourceID();
|
||||||
|
|
||||||
|
}
|
||||||
|
_layerStyleGroupBySourceID() {
|
||||||
|
const sourceStyles = [];
|
||||||
|
// 支持VectorLayer
|
||||||
|
for (const layerId in this.layerStyles) {
|
||||||
|
const sourceID = this.layerStyles[layerId].sourceOption.id;
|
||||||
|
const sourcelayer = this.layerStyles[layerId].sourceOption.parser.sourceLayer;
|
||||||
|
if (!sourceStyles[sourceID]) sourceStyles[sourceID] = {};
|
||||||
|
if (!sourceStyles[sourceID][sourcelayer]) sourceStyles[sourceID][sourcelayer] = [];
|
||||||
|
sourceStyles[sourceID][sourcelayer].push(this.layerStyles[layerId]);
|
||||||
|
}
|
||||||
|
this.sourceStyles = sourceStyles;
|
||||||
|
}
|
||||||
|
update(parameters) {
|
||||||
|
this.addTileStyle(parameters);
|
||||||
|
for (const key in this._sourceCaches) {
|
||||||
|
this._sourceCaches[key].update(this.sourceStyles[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
addMapEvent() {
|
||||||
|
this.mapEventHander = () => {
|
||||||
|
for (const key in this._sourceCaches) {
|
||||||
|
this._sourceCaches[key].update(this.sourceStyles[key]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.scene.on('zoomchange', this.mapEventHander);
|
||||||
|
this.scene.on('dragend', this.mapEventHander);
|
||||||
|
}
|
||||||
|
clearMapEvent() {
|
||||||
|
this.scene.off('zoomchange', this.mapEventHander);
|
||||||
|
this.scene.off('dragend', this.mapEventHander);
|
||||||
|
}
|
||||||
|
// 计算视野内的瓦片坐标
|
||||||
|
_calculateTileIDs() {
|
||||||
|
this.updateTileList = [];
|
||||||
|
const pixelBounds = this._getPixelBounds();
|
||||||
|
const tileRange = this._pxBoundsToTileRange(pixelBounds);
|
||||||
|
const margin = this.get('keepBuffer');
|
||||||
|
this.noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([ margin, -margin ]),
|
||||||
|
tileRange.getTopRight().add([ margin, -margin ]));
|
||||||
|
if (!(isFinite(tileRange.min.x) &&
|
||||||
|
isFinite(tileRange.min.y) &&
|
||||||
|
isFinite(tileRange.max.x) &&
|
||||||
|
isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
|
||||||
|
for (let j = tileRange.min.y; j <= tileRange.max.y; j++) {
|
||||||
|
for (let i = tileRange.min.x; i <= tileRange.max.x; i++) {
|
||||||
|
const coords = [ i, j, this.tileZoom ];
|
||||||
|
this.tileList.push(coords);
|
||||||
|
this._tileMap[coords.join('_')] = coords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_getPixelBounds() {
|
||||||
|
const viewPort = this.scene.getBounds().toBounds();
|
||||||
|
const NE = viewPort.getNorthEast();
|
||||||
|
const SW = viewPort.getSouthWest();
|
||||||
|
const zoom = this.tileZoom;
|
||||||
|
const center = this.scene.getCenter();
|
||||||
|
const NEPoint = this.scene.crs.lngLatToPoint(toLngLat(NE.lng, NE.lat), zoom);
|
||||||
|
const SWPoint = this.scene.crs.lngLatToPoint(toLngLat(SW.lng, SW.lat), zoom);
|
||||||
|
const centerPoint = this.scene.crs.lngLatToPoint(toLngLat(center.lng, center.lat), zoom);
|
||||||
|
const topHeight = centerPoint.y - NEPoint.y;
|
||||||
|
const bottomHeight = SWPoint.y - centerPoint.y;
|
||||||
|
// 跨日界线的情况
|
||||||
|
let leftWidth;
|
||||||
|
let rightWidth;
|
||||||
|
if (center.lng - NE.lng > 0 || center.lng - SW.lng < 0) {
|
||||||
|
const width = Math.pow(2, zoom) * 256 / 360 * (180 - NE.lng) + Math.pow(2, zoom) * 256 / 360 * (SW.lng + 180);
|
||||||
|
if (center.lng - NE.lng > 0) { // 日界线在右侧
|
||||||
|
leftWidth = Math.pow(2, zoom) * 256 / 360 * (center.lng - NE.lng);
|
||||||
|
rightWidth = width - leftWidth;
|
||||||
|
} else {
|
||||||
|
rightWidth = Math.pow(2, zoom) * 256 / 360 * (SW.lng - center.lng);
|
||||||
|
leftWidth = width - rightWidth;
|
||||||
|
}
|
||||||
|
} else { // 不跨日界线
|
||||||
|
leftWidth = Math.pow(2, zoom) * 256 / 360 * (center.lng - SW.lng);
|
||||||
|
rightWidth = Math.pow(2, zoom) * 256 / 360 * (NE.lng - center.lng);
|
||||||
|
}
|
||||||
|
const pixelBounds = new Bounds(centerPoint.subtract(leftWidth, topHeight), centerPoint.add(rightWidth, bottomHeight));
|
||||||
|
return pixelBounds;
|
||||||
|
}
|
||||||
|
_pxBoundsToTileRange(pixelBounds) {
|
||||||
|
return new Bounds(
|
||||||
|
pixelBounds.min.divideBy(256).floor(),
|
||||||
|
pixelBounds.max.divideBy(256).ceil().subtract([ 1, 1 ])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -47,10 +47,10 @@ export default class Tile extends Base {
|
||||||
}
|
}
|
||||||
requestTileAsync(done) {
|
requestTileAsync(done) {
|
||||||
// 获取数据
|
// 获取数据
|
||||||
this.layer.workerTileSource.loadTile({
|
// this.layer.workerTileSource.loadTile({
|
||||||
tile: this._tile,
|
// tile: this._tile,
|
||||||
url: this.layer.tileSource.getRequestUrl(this._tile[0], this._tile[1], this._tile[2])
|
// url: this.layer.tileSource.getRequestUrl(this._tile[0], this._tile[1], this._tile[2])
|
||||||
});
|
// });
|
||||||
const data = this.layer.tileSource.getTileData(this._tile[0], this._tile[1], this._tile[2]);
|
const data = this.layer.tileSource.getTileData(this._tile[0], this._tile[1], this._tile[2]);
|
||||||
if (data.loaded) {
|
if (data.loaded) {
|
||||||
done(data.data);
|
done(data.data);
|
||||||
|
|
|
@ -2,7 +2,6 @@ import Layer from '../../core/layer';
|
||||||
import Util from '../../util';
|
import Util from '../../util';
|
||||||
import diff from '../../util/diff';
|
import diff from '../../util/diff';
|
||||||
import TileSource from '../../source/tile_source';
|
import TileSource from '../../source/tile_source';
|
||||||
import TileWorkerSource from '../../source/tile_worker_source';
|
|
||||||
import * as THREE from '../../core/three';
|
import * as THREE from '../../core/three';
|
||||||
import Controller from '../../core/controller/index';
|
import Controller from '../../core/controller/index';
|
||||||
import Global from '../../global';
|
import Global from '../../global';
|
||||||
|
@ -27,9 +26,6 @@ export default class TileLayer extends Layer {
|
||||||
this.tileList = {};
|
this.tileList = {};
|
||||||
this.type = this.get('layerType');
|
this.type = this.get('layerType');
|
||||||
this.workerPool = this.scene.workerPool;
|
this.workerPool = this.scene.workerPool;
|
||||||
this.workerTileSource = new TileWorkerSource({
|
|
||||||
workerPool: this.scene.workerPool
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
shape(field, values) {
|
shape(field, values) {
|
||||||
const layerType = this.get('layerType');
|
const layerType = this.get('layerType');
|
||||||
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
import Base from '../core/base';
|
||||||
|
import TileDataCache from '../source/tile_data_cache';
|
||||||
|
import VectorTileSource from './vector_tile_source';
|
||||||
|
import { toLngLat, Bounds } from '@antv/geo-coord';
|
||||||
|
// 统一管理 source 添加,管理,更新
|
||||||
|
export default class SouceCache extends Base {
|
||||||
|
constructor(scene, cfg) {
|
||||||
|
super({
|
||||||
|
cacheLimit: 50,
|
||||||
|
minZoom: 0,
|
||||||
|
maxZoom: 18,
|
||||||
|
keepBuffer: 2,
|
||||||
|
...cfg
|
||||||
|
});
|
||||||
|
this._tileMap = {};// 视野内瓦片坐标序列
|
||||||
|
this._tileList = {}; // 正在使用的瓦片坐标,记录瓦片的使用状态
|
||||||
|
this.scene = scene;
|
||||||
|
// TODO 销毁函数
|
||||||
|
this._tileCache = new TileDataCache(this.get('cacheLimit'), () => { });
|
||||||
|
this._source = new VectorTileSource(cfg, this.scene.style.WorkerController);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除视野外的瓦片,计算新增的瓦片数据
|
||||||
|
* @param {*}tileMap 瓦片列表
|
||||||
|
*/
|
||||||
|
|
||||||
|
update(layercfg) {
|
||||||
|
if (!layercfg && this.layercfg) return;
|
||||||
|
this._layercfg = layercfg;
|
||||||
|
this._calculateTileIDs();
|
||||||
|
this.updateList = this._getNewTiles(this._tileMap);// 计算新增瓦片
|
||||||
|
this._pruneTiles();
|
||||||
|
for (let i = 0; i < this.updateList.length; i++) {
|
||||||
|
// 瓦片相关参数
|
||||||
|
const tileId = this.updateList[i];
|
||||||
|
this._source.loadTile(tileId, res => {
|
||||||
|
this._tileList[tileId].active = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 计算视野内的瓦片坐标
|
||||||
|
_calculateTileIDs() {
|
||||||
|
this._tileMap = {};
|
||||||
|
const zoom = Math.floor(this.scene.getZoom()) - 1;
|
||||||
|
const minSourceZoom = this.get('minZoom');
|
||||||
|
const maxSourceZoom = this.get('maxZoom');
|
||||||
|
this.tileZoom = zoom > maxSourceZoom ? maxSourceZoom : zoom;
|
||||||
|
const pixelBounds = this._getPixelBounds();
|
||||||
|
const tileRange = this._pxBoundsToTileRange(pixelBounds);
|
||||||
|
const margin = this.get('keepBuffer');
|
||||||
|
this._noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([ margin, -margin ]),
|
||||||
|
tileRange.getTopRight().add([ margin, -margin ]));
|
||||||
|
if (!(isFinite(tileRange.min.x) &&
|
||||||
|
isFinite(tileRange.min.y) &&
|
||||||
|
isFinite(tileRange.max.x) &&
|
||||||
|
isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
|
||||||
|
for (let j = tileRange.min.y; j <= tileRange.max.y; j++) {
|
||||||
|
for (let i = tileRange.min.x; i <= tileRange.max.x; i++) {
|
||||||
|
const coords = [ i, j, this.tileZoom ];
|
||||||
|
this._tileMap[coords.join('_')] = coords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const currentZoom = this.scene.getZoom();
|
||||||
|
if (currentZoom < minSourceZoom) {
|
||||||
|
this._removeTiles();
|
||||||
|
// 小于source最小范围不在处理
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_getPixelBounds() {
|
||||||
|
const viewPort = this.scene.getBounds().toBounds();
|
||||||
|
const NE = viewPort.getNorthEast();
|
||||||
|
const SW = viewPort.getSouthWest();
|
||||||
|
const zoom = this.tileZoom;
|
||||||
|
const center = this.scene.getCenter();
|
||||||
|
const NEPoint = this.scene.crs.lngLatToPoint(toLngLat(NE.lng, NE.lat), zoom);
|
||||||
|
const SWPoint = this.scene.crs.lngLatToPoint(toLngLat(SW.lng, SW.lat), zoom);
|
||||||
|
const centerPoint = this.scene.crs.lngLatToPoint(toLngLat(center.lng, center.lat), zoom);
|
||||||
|
const topHeight = centerPoint.y - NEPoint.y;
|
||||||
|
const bottomHeight = SWPoint.y - centerPoint.y;
|
||||||
|
// 跨日界线的情况
|
||||||
|
let leftWidth;
|
||||||
|
let rightWidth;
|
||||||
|
if (center.lng - NE.lng > 0 || center.lng - SW.lng < 0) {
|
||||||
|
const width = Math.pow(2, zoom) * 256 / 360 * (180 - NE.lng) + Math.pow(2, zoom) * 256 / 360 * (SW.lng + 180);
|
||||||
|
if (center.lng - NE.lng > 0) { // 日界线在右侧
|
||||||
|
leftWidth = Math.pow(2, zoom) * 256 / 360 * (center.lng - NE.lng);
|
||||||
|
rightWidth = width - leftWidth;
|
||||||
|
} else {
|
||||||
|
rightWidth = Math.pow(2, zoom) * 256 / 360 * (SW.lng - center.lng);
|
||||||
|
leftWidth = width - rightWidth;
|
||||||
|
}
|
||||||
|
} else { // 不跨日界线
|
||||||
|
leftWidth = Math.pow(2, zoom) * 256 / 360 * (center.lng - SW.lng);
|
||||||
|
rightWidth = Math.pow(2, zoom) * 256 / 360 * (NE.lng - center.lng);
|
||||||
|
}
|
||||||
|
const pixelBounds = new Bounds(centerPoint.subtract(leftWidth, topHeight), centerPoint.add(rightWidth, bottomHeight));
|
||||||
|
return pixelBounds;
|
||||||
|
}
|
||||||
|
_pxBoundsToTileRange(pixelBounds) {
|
||||||
|
return new Bounds(
|
||||||
|
pixelBounds.min.divideBy(256).floor(),
|
||||||
|
pixelBounds.max.divideBy(256).ceil().subtract([ 1, 1 ])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadTile(tile, callback) {
|
||||||
|
return this._source.loadTile(tile, callback);
|
||||||
|
|
||||||
|
}
|
||||||
|
reload() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_reloadTile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
_removeTile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
clearTiles() {
|
||||||
|
|
||||||
|
}
|
||||||
|
_getNewTiles(tileMap) {
|
||||||
|
const center = this.scene.getCenter();
|
||||||
|
const centerPoint = this.scene.crs.lngLatToPoint(toLngLat(center.lng, center.lat), this.tileZoom);
|
||||||
|
const centerXY = centerPoint.divideBy(256).floor();
|
||||||
|
const newTileList = [];
|
||||||
|
for (const tile in tileMap) {
|
||||||
|
if (!this._tileList[tile]) {
|
||||||
|
this._tileList[tile] = {
|
||||||
|
current: true,
|
||||||
|
active: false,
|
||||||
|
coords: tile.split('_')
|
||||||
|
};
|
||||||
|
newTileList.push(tile);
|
||||||
|
} else {
|
||||||
|
this._tileList[tile].current = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const tile in this._tileList) { // 更新tilelist状态
|
||||||
|
this._tileList[tile].current = !!this._tileMap[tile];
|
||||||
|
this._tileList[tile].retain = !!this._tileMap[tile];
|
||||||
|
}
|
||||||
|
newTileList.sort((a, b) => {
|
||||||
|
const tile1 = a;
|
||||||
|
const tile2 = b;
|
||||||
|
const d1 = Math.pow((tile1[0] * 1 - centerXY.x), 2) + Math.pow((tile1[1] * 1 - centerXY.y), 2);
|
||||||
|
const d2 = Math.pow((tile2[0] * 1 - centerXY.x), 2) + Math.pow((tile2[1] * 1 - centerXY.y), 2);
|
||||||
|
return d1 - d2;
|
||||||
|
});
|
||||||
|
return newTileList;
|
||||||
|
}
|
||||||
|
_pruneTiles() {
|
||||||
|
|
||||||
|
for (const key in this._tileList) {
|
||||||
|
const tile = this._tileList[key];
|
||||||
|
if (tile.current && !tile.active) {
|
||||||
|
const [ x, y, z ] = key.split('_').map(v => v * 1);
|
||||||
|
if (!this._retainParent(x, y, z, z - 5)) {
|
||||||
|
this._retainChildren(x, y, z, z + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
this._removeOutTiles();
|
||||||
|
}
|
||||||
|
_retainParent(x, y, z, minZoom) {
|
||||||
|
const x2 = Math.floor(x / 2);
|
||||||
|
const y2 = Math.floor(y / 2);
|
||||||
|
const z2 = z - 1;
|
||||||
|
const tile = this._tileList[[ x2, y2, z2 ].join('_')];
|
||||||
|
if (tile && tile.active) {
|
||||||
|
tile.retain = true;
|
||||||
|
tile.current = true;
|
||||||
|
return true;
|
||||||
|
} else if (tile && tile.loaded) {
|
||||||
|
tile.retain = true;
|
||||||
|
}
|
||||||
|
if (z2 > minZoom) {
|
||||||
|
return this._retainParent(x2, y2, z2, minZoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
_retainChildren(x, y, z, maxZoom) {
|
||||||
|
for (let i = 2 * x; i < 2 * x + 2; i++) {
|
||||||
|
for (let j = 2 * y; j < 2 * y + 2; j++) {
|
||||||
|
const key = [ i, j, z + 1 ].join('_');
|
||||||
|
const tile = this._tileList[key];
|
||||||
|
if (tile && tile.active) {
|
||||||
|
tile.retain = true;
|
||||||
|
tile.current = true;
|
||||||
|
continue;
|
||||||
|
} else if (tile && tile.loaded) {
|
||||||
|
tile.retain = true;
|
||||||
|
tile.current = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z + 1 < maxZoom) {
|
||||||
|
this._retainChildren(i, j, z + 1, maxZoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_removeOutTiles() {
|
||||||
|
// 移除视野外的tile
|
||||||
|
for (const key in this._tileList) {
|
||||||
|
!this._tileList[key].retain && delete this._tileList[key];
|
||||||
|
// 移除对应的数据
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,17 +0,0 @@
|
||||||
import Base from '../core/base';
|
|
||||||
|
|
||||||
export default class TileWorkerSource extends Base {
|
|
||||||
constructor(cfg) {
|
|
||||||
super(cfg);
|
|
||||||
this.workerPool = this.get('workerPool');
|
|
||||||
this.type = 'tile';
|
|
||||||
}
|
|
||||||
loadTile({ tile, url }) {
|
|
||||||
this.get('sourceCfg').parser.tile = tile;
|
|
||||||
return this.workerPool.runTask({
|
|
||||||
url,
|
|
||||||
attrs: this.get('attrs'),
|
|
||||||
sourceCfg: this.get('sourceCfg')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Base from '../core/base';
|
||||||
|
|
||||||
|
export default class VectorTileSource extends Base{
|
||||||
|
constructor(cfg, workerController) {
|
||||||
|
super({
|
||||||
|
type: 'vector',
|
||||||
|
...cfg
|
||||||
|
});
|
||||||
|
this.workerController = workerController;
|
||||||
|
}
|
||||||
|
loadTile(tile, callback) {
|
||||||
|
const params = {
|
||||||
|
id: tile,
|
||||||
|
};
|
||||||
|
this.workerController.send('loadTile', params, done.bind(this));
|
||||||
|
function done(err,data) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
abortTile(tile) {
|
||||||
|
this.workerController.send('abortTile', { uid: tile.uid, type: this.type, source: this.id }, undefined, tile.workerID);
|
||||||
|
}
|
||||||
|
unloadTile(tile) {
|
||||||
|
this.workerController.send('removeTile', { uid: tile.uid, type: this.type, source: this.id }, undefined, tile.workerID);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
import Base from '../core/base';
|
||||||
|
import { getArrayBuffer } from '../util/ajax';
|
||||||
|
const tileURLRegex = /\{([zxy])\}/g;
|
||||||
|
import PBF from 'pbf';
|
||||||
|
import * as VectorParser from '@mapbox/vector-tile';
|
||||||
|
import WorkerTile from '../worker/workerTile';
|
||||||
|
// import WorkerTile from '../worker/workerTile';
|
||||||
|
export default class VectorTileSource extends Base {
|
||||||
|
constructor(cfg, workerController) {
|
||||||
|
super({
|
||||||
|
type: 'vector',
|
||||||
|
...cfg
|
||||||
|
});
|
||||||
|
this.workerController = workerController;
|
||||||
|
}
|
||||||
|
loadVectorTile(params, callback) {
|
||||||
|
const request = getArrayBuffer(params.request, (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
} else if (data) {
|
||||||
|
callback(null, {
|
||||||
|
vectorTile: new VectorParser.VectorTile(new PBF(data)),
|
||||||
|
rawData: data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
request.cancel();
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
loadTile(params, callback) {
|
||||||
|
console.log(params);
|
||||||
|
const workerTile = new WorkerTile(params);
|
||||||
|
workerTile.abort = this.loadVectorData(params, (err, response) => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
abortTile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
unloadTile() {
|
||||||
|
|
||||||
|
}
|
||||||
|
hasTransition() {
|
||||||
|
|
||||||
|
}
|
||||||
|
_getTileURL(urlParams) {
|
||||||
|
if (!urlParams.s) {
|
||||||
|
// Default to a random choice of a, b or c
|
||||||
|
urlParams.s = String.fromCharCode(97 + Math.floor(Math.random() * 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
tileURLRegex.lastIndex = 0;
|
||||||
|
return this.urlTemplate.replace(tileURLRegex, function(value, key) {
|
||||||
|
return urlParams[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { serialize } from './worker_transform';
|
||||||
|
function bindAll(fns, context) {
|
||||||
|
fns.forEach(fn => {
|
||||||
|
if (!context[fn]) { return; }
|
||||||
|
context[fn] = context[fn].bind(context);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Actor {
|
||||||
|
constructor(target, parent, mapId) {
|
||||||
|
this.target = target;
|
||||||
|
this.parent = parent;
|
||||||
|
this.mapId = mapId;
|
||||||
|
this.callbacks = {};
|
||||||
|
this.callbackID = 0;
|
||||||
|
bindAll(['receive'], this);
|
||||||
|
this.target.addEventListener('message', this.receive, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
send(type, data, callback, targetMapId) {
|
||||||
|
const id = callback ? `${this.mapId}_${this.callbackID++}` : null;
|
||||||
|
if (callback) this.callbacks[id] = callback;
|
||||||
|
const buffers = [];
|
||||||
|
this.target.postMessage({
|
||||||
|
targetMapId,
|
||||||
|
sourceMapId: this.mapId,
|
||||||
|
type,
|
||||||
|
id: String(id),
|
||||||
|
data
|
||||||
|
}, buffers);
|
||||||
|
if (callback) {
|
||||||
|
return {
|
||||||
|
cancel: () => this.target.postMessage({
|
||||||
|
targetMapId,
|
||||||
|
sourceMapId: this.mapId,
|
||||||
|
type: '<cancel>',
|
||||||
|
id: String(id)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
receive(message) {
|
||||||
|
const data = message.data;
|
||||||
|
const id = data.id;
|
||||||
|
if (Object.keys(this.callbacks).length === 0) {
|
||||||
|
this.target.postMessage({ // worker向主线程发送结果数据
|
||||||
|
sourceMapId: this.mapId,
|
||||||
|
type: '<response>',
|
||||||
|
id: String(id),
|
||||||
|
data: 'callback'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof data.id !== 'undefined' && this.parent[data.type]) {
|
||||||
|
console.log(data.type);
|
||||||
|
}
|
||||||
|
// TODO worker 处理数据 创建worker source 根据类型调用响应的方法
|
||||||
|
if (data.type === '<response>' || data.type === '<cancel>') {
|
||||||
|
this.callbacks[id](id);
|
||||||
|
delete this.callbacks[id]; // 回调执行
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,45 +1,60 @@
|
||||||
import Source from '../core/source';
|
import VectorTileWorkerSource from '../source/vector_tile_worker_source';
|
||||||
// import Controller from '../core/controller/index';
|
import Actor from './actor';
|
||||||
import { getArrayBuffer } from '../util/ajax';
|
|
||||||
|
|
||||||
|
// 统一管理workerSource 实例化
|
||||||
export default class Worker {
|
export default class Worker {
|
||||||
constructor(self) {
|
constructor(self) {
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.self.addEventListener('message', cfg => {
|
this.actor = new Actor(self, this);
|
||||||
this.loadTile(cfg.data);
|
this.workerSourceTypes = {
|
||||||
});
|
vector: VectorTileWorkerSource
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTile(cfg) {
|
loadTile(cfg) {
|
||||||
// const tileSource = new TileSource(cfg.data, cfg.cfg);
|
|
||||||
getArrayBuffer({ url: cfg.url }, (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
this.self.postMessage(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const tileData = this._generateSource(cfg, data.data);
|
|
||||||
console.log(tileData);
|
|
||||||
const uInt8Array = new Uint8Array(1024 * 1024 * 32); // 32MB
|
|
||||||
for (let i = 0; i < uInt8Array.length; ++i) {
|
|
||||||
uInt8Array[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.time('postmessage');
|
|
||||||
const b = function() {
|
|
||||||
return 'update';
|
|
||||||
};
|
|
||||||
this.self.postMessage({ a:
|
|
||||||
uInt8Array.buffer,
|
|
||||||
update: b
|
|
||||||
}, [ uInt8Array.buffer, b ]);
|
|
||||||
console.timeEnd('postmessage');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
_generateSource(cfg, data) {
|
setLayers(mapId, layercfgs,callback) {
|
||||||
const tileData = new Source({
|
|
||||||
...cfg.sourceCfg,
|
}
|
||||||
data
|
updateLayers(id, params, callback) {
|
||||||
});
|
|
||||||
return tileData;
|
}
|
||||||
|
/**
|
||||||
|
* 获取workerSource
|
||||||
|
* @param {string} mapId WorkerPool Id
|
||||||
|
* @param {string} type 瓦片类型 目前支持Vector
|
||||||
|
* @param {string} source souce ID
|
||||||
|
* @return {*} WorkerSource
|
||||||
|
*/
|
||||||
|
getWorkerSource(mapId, type, source) {
|
||||||
|
if (!this.workerSources[mapId]) {
|
||||||
|
this.workerSources[mapId] = {};
|
||||||
|
}
|
||||||
|
if (!this.workerSources[mapId][type]) {
|
||||||
|
this.workerSources[mapId][type] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.workerSources[mapId][type][source]) {
|
||||||
|
// 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) => {
|
||||||
|
this.actor.send(type, data, callback, mapId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.workerSources[mapId][type][source] = new this.workerSourceTypes[type](actor, this.getLayerIndex(mapId));
|
||||||
|
}
|
||||||
|
return this.workerSources[mapId][type][source];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.worker = new Worker(self);
|
self.worker = new Worker(self);
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
export default class WorkerTile {
|
||||||
|
constructor(tile) {
|
||||||
|
this.id = tile.id;
|
||||||
|
}
|
||||||
|
parse(data, layerstyle, actor, callback) {
|
||||||
|
this.status = 'parsing';
|
||||||
|
this.data = data;
|
||||||
|
const buckets = {};
|
||||||
|
// 根据source
|
||||||
|
for (const sourcelayer in layerstyle) {
|
||||||
|
for (let i = 0; i < layerstyle[sourcelayer].length; i++) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.status = 'done';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
|
||||||
|
import Actor from './actor';
|
||||||
|
let id = 1;
|
||||||
|
function asyncAll(
|
||||||
|
array,
|
||||||
|
fn,
|
||||||
|
callback
|
||||||
|
) {
|
||||||
|
if (!array.length) { return callback(null, []); }
|
||||||
|
let remaining = array.length;
|
||||||
|
const results = new Array(array.length);
|
||||||
|
let error = null;
|
||||||
|
array.forEach((item, i) => {
|
||||||
|
fn(item, (err, result) => {
|
||||||
|
if (err) error = err;
|
||||||
|
results[i] = ((result));
|
||||||
|
if (--remaining === 0) callback(error, results);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class WorkerController {
|
||||||
|
constructor(workerPool, parent) {
|
||||||
|
this.workerPool = workerPool;
|
||||||
|
this.actors = [];
|
||||||
|
this.currentActor = 0;
|
||||||
|
this.id = id++;
|
||||||
|
const workers = this.workerPool.acquire(this.id);
|
||||||
|
|
||||||
|
for (let i = 0; i < workers.length; i++) {
|
||||||
|
const worker = workers[i];
|
||||||
|
const actor = new WorkerController.Actor(worker, parent, this.id);
|
||||||
|
actor.name = `Worker ${i}`;
|
||||||
|
this.actors.push(actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast a message to all Workers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
broadcast(type, data, cb) {
|
||||||
|
cb = cb || function() { };
|
||||||
|
asyncAll(this.actors, (actor, done) => {
|
||||||
|
actor.send(type, data, done);
|
||||||
|
}, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
send(type, data, callback, targetID) {
|
||||||
|
console.log('消息发送', data);
|
||||||
|
if (typeof targetID !== 'number' || isNaN(targetID)) {
|
||||||
|
// Use round robin to send requests to web workers.
|
||||||
|
targetID = this.currentActor = (this.currentActor + 1) % this.actors.length;
|
||||||
|
}
|
||||||
|
this.actors[targetID].send(type, data, callback, targetID);
|
||||||
|
return targetID;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.actors.forEach(actor => { actor.remove(); });
|
||||||
|
this.actors = [];
|
||||||
|
this.workerPool.release(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
WorkerController.Actor = Actor;
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import WebWorker from './web_worker';
|
||||||
|
export default class WorkerPool {
|
||||||
|
constructor(workerCount) {
|
||||||
|
this.workerCount = workerCount || Math.max(Math.floor(window.navigator.hardwareConcurrency / 2), 1);
|
||||||
|
this.workers = []; // worker线程池
|
||||||
|
this.workerQueue = []; // 任务队列
|
||||||
|
this._initWorker(); // 初始化线程池
|
||||||
|
}
|
||||||
|
_initWorker() {
|
||||||
|
while (this.workers.length < this.workerCount) {
|
||||||
|
this.workers.push(new WebWorker());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runTask(payload) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (this.workers.length > 0) {
|
||||||
|
const worker = this.workers.shift(); // 从线程池取出一个worker
|
||||||
|
worker.postMessage(payload); // 向线程发送数据
|
||||||
|
const workerCallback = e => {
|
||||||
|
resolve(e.data); // 成功则返回数据
|
||||||
|
// 移除事件监听
|
||||||
|
worker.removeEventListener('message', workerCallback);
|
||||||
|
// 重新放回线程池
|
||||||
|
this.workers.push(worker);
|
||||||
|
// 如果任务队列的数据还有则从任务队列继续取数据执行任务
|
||||||
|
if (this.workerQueue.length > 0) {
|
||||||
|
const queueData = this.workerQueue.shift();
|
||||||
|
this.runTask(queueData.payload).then(data => {
|
||||||
|
queueData.resolve(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 监听worker事件
|
||||||
|
worker.addEventListener('message', workerCallback);
|
||||||
|
worker.addEventListener('error', e => {
|
||||||
|
reject('filename:' + e.filename + '\nmessage:' + e.message + '\nlineno:' + e.lineno);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果线程池都被占用,则将数据丢入任务队列,并保存对应的resolve和reject
|
||||||
|
this.workerQueue.push({
|
||||||
|
payload,
|
||||||
|
resolve,
|
||||||
|
reject
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
release() {
|
||||||
|
this.workers.forEach(worker => {
|
||||||
|
worker.terminate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,54 +1,38 @@
|
||||||
import WebWorker from './web_worker';
|
import WebWorker from './web_worker';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a worker pool.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
export default class WorkerPool {
|
export default class WorkerPool {
|
||||||
constructor(workerCount) {
|
|
||||||
this.workerCount = workerCount || Math.max(Math.floor(window.navigator.hardwareConcurrency / 2), 1);
|
constructor() {
|
||||||
this.workers = []; // worker线程池
|
this.active = {};
|
||||||
this.workerQueue = []; // 任务队列
|
|
||||||
this._initWorker(); // 初始化线程池
|
|
||||||
}
|
}
|
||||||
_initWorker() {
|
|
||||||
while (this.workers.length < this.workerCount) {
|
acquire(mapId) {
|
||||||
this.workers.push(new WebWorker());
|
if (!this.workers) {
|
||||||
}
|
// Lazily look up the value of mapboxgl.workerCount so that
|
||||||
}
|
// client code has had a chance to set it.
|
||||||
runTask(payload) {
|
this.workers = [];
|
||||||
return new Promise((resolve, reject) => {
|
while (this.workers.length < WorkerPool.workerCount) {
|
||||||
if (this.workers.length > 0) {
|
this.workers.push(new WebWorker());
|
||||||
const worker = this.workers.shift(); // 从线程池取出一个worker
|
|
||||||
worker.postMessage(payload); // 向线程发送数据
|
|
||||||
const workerCallback = e => {
|
|
||||||
resolve(e.data); // 成功则返回数据
|
|
||||||
// 移除事件监听
|
|
||||||
worker.removeEventListener('message', workerCallback);
|
|
||||||
// 重新放回线程池
|
|
||||||
this.workers.push(worker);
|
|
||||||
// 如果任务队列的数据还有则从任务队列继续取数据执行任务
|
|
||||||
if (this.workerQueue.length > 0) {
|
|
||||||
const queueData = this.workerQueue.shift();
|
|
||||||
this.runTask(queueData.payload).then(data => {
|
|
||||||
queueData.resolve(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// 监听worker事件
|
|
||||||
worker.addEventListener('message', workerCallback);
|
|
||||||
worker.addEventListener('error', e => {
|
|
||||||
reject('filename:' + e.filename + '\nmessage:' + e.message + '\nlineno:' + e.lineno);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 如果线程池都被占用,则将数据丢入任务队列,并保存对应的resolve和reject
|
|
||||||
this.workerQueue.push({
|
|
||||||
payload,
|
|
||||||
resolve,
|
|
||||||
reject
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
this.active[mapId] = true;
|
||||||
|
return this.workers.slice();
|
||||||
}
|
}
|
||||||
release() {
|
|
||||||
this.workers.forEach(worker => {
|
release(mapId) {
|
||||||
worker.terminate();
|
delete this.active[mapId];
|
||||||
});
|
if (Object.keys(this.active).length === 0) {
|
||||||
|
this.workers.forEach(w => {
|
||||||
|
w.terminate();
|
||||||
|
});
|
||||||
|
this.workers = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WorkerPool.workerCount = Math.max(Math.floor(window.navigator.hardwareConcurrency / 2), 1);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
export function serialize() {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deserialize() {
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue