diff --git a/demos/06_text.html b/demos/06_text.html
index 3fd2bc4f61..45708e187f 100644
--- a/demos/06_text.html
+++ b/demos/06_text.html
@@ -29,7 +29,7 @@ const scene = new L7.Scene({
});
window.scene = scene;
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({
zIndex: 2
})
diff --git a/demos/08_arc_line.html b/demos/08_arc_line.html
index 8a130d82e9..553b9a0d69 100644
--- a/demos/08_arc_line.html
+++ b/demos/08_arc_line.html
@@ -51,7 +51,7 @@ scene.on('loaded', () => {
})
//.animate({enable:true})
.render();
- console.log(layer);
+
/**
scene.LineLayer({
zIndex: 2
diff --git a/demos/workerdemo.html b/demos/workerdemo.html
new file mode 100644
index 0000000000..d17462f3d6
--- /dev/null
+++ b/demos/workerdemo.html
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+ hexagon demo
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/layer.js b/src/core/layer.js
index b1c8cfcac2..ad705463f3 100644
--- a/src/core/layer.js
+++ b/src/core/layer.js
@@ -69,6 +69,7 @@ export default class Layer extends Base {
this._object3D.renderOrder = this.get('zIndex') || 0;
this._mapEventHandlers = [];
const layerId = this._getUniqueId();
+ this.set('layerId', layerId);
this.layerId = layerId;
this._activeIds = null;
const world = scene._engine.world;
@@ -127,7 +128,19 @@ export default class Layer extends Base {
this.set('visible', visible);
this._object3D.visible = this.get('visible');
}
+ // 兼容瓦片source,非瓦片source
+
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) {
this.layerSource = data;
return this;
@@ -136,9 +149,6 @@ export default class Layer extends Base {
cfg.mapType = this.scene.mapType;
cfg.zoom = this.scene.getZoom();
this.layerSource = new source(cfg);
- // this.scene.workerPool.runTask(cfg).then(data => {
- // console.log(data);
- // });
return this;
}
color(field, values) {
@@ -281,7 +291,7 @@ export default class Layer extends Base {
options[attrName] = attrCfg;
}
_createAttrOption(attrName, field, cfg, defaultValues) {
-
+
const attrCfg = {};
attrCfg.field = field;
if (cfg) {
@@ -305,6 +315,10 @@ export default class Layer extends Base {
}
render() {
+ if (this.get('layerType') === 'tile') {
+ this.scene.style.update(this._attrs);
+ return this;
+ }
this.init();
this.scene._engine.update();
return this;
diff --git a/src/core/scene.js b/src/core/scene.js
index 86d3afca35..efc46c4976 100644
--- a/src/core/scene.js
+++ b/src/core/scene.js
@@ -3,12 +3,13 @@ import { LAYER_MAP } from '../layer';
import Base from './base';
import LoadImage from './image';
import FontAtlasManager from '../geom/buffer/point/text/font-manager';
-import WorkerPool from '../worker/worker_pool';
// import { MapProvider } from '../map/AMap';
import { getMap } from '../map/index';
import Global from '../global';
import { getInteraction } from '../interaction/index';
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 {
getDefaultCfg() {
return Global.scene;
@@ -16,6 +17,7 @@ export default class Scene extends Base {
constructor(cfg) {
super(cfg);
this._initMap();
+ this.crs = epsg3857;
// this._initAttribution(); // 暂时取消,后面作为组件去加载
this.addImage();
this.fontAtlasManager = new FontAtlasManager();
@@ -27,7 +29,6 @@ export default class Scene extends Base {
this._engine = new Engine(mapContainer, this);
// this.registerMapEvent();
this._engine.run();
- this.workerPool = new WorkerPool();
compileBuiltinModules();
}
// 为pickup场景添加 object 对象
@@ -54,6 +55,7 @@ export default class Scene extends Base {
const interaction = new Ctor({ layer: this });
interaction._onHashChange();
}
+ this.style = new Style(this, {});
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) {
if (this.map) { this.map.on(type, hander); }
super.on(type, hander);
diff --git a/src/core/style.js b/src/core/style.js
new file mode 100644
index 0000000000..2dbbf858de
--- /dev/null
+++ b/src/core/style.js
@@ -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 ])
+ );
+ }
+
+}
diff --git a/src/layer/tile/tile.js b/src/layer/tile/tile.js
index 846b85ea78..6311e8698f 100644
--- a/src/layer/tile/tile.js
+++ b/src/layer/tile/tile.js
@@ -47,10 +47,10 @@ export default class Tile extends Base {
}
requestTileAsync(done) {
// 获取数据
- this.layer.workerTileSource.loadTile({
- tile: this._tile,
- url: this.layer.tileSource.getRequestUrl(this._tile[0], this._tile[1], this._tile[2])
- });
+ // this.layer.workerTileSource.loadTile({
+ // tile: this._tile,
+ // 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]);
if (data.loaded) {
done(data.data);
diff --git a/src/layer/tile/tile_layer.js b/src/layer/tile/tile_layer.js
index 821dd5b2e2..8779fc63dd 100644
--- a/src/layer/tile/tile_layer.js
+++ b/src/layer/tile/tile_layer.js
@@ -2,7 +2,6 @@ import Layer from '../../core/layer';
import Util from '../../util';
import diff from '../../util/diff';
import TileSource from '../../source/tile_source';
-import TileWorkerSource from '../../source/tile_worker_source';
import * as THREE from '../../core/three';
import Controller from '../../core/controller/index';
import Global from '../../global';
@@ -27,9 +26,6 @@ export default class TileLayer extends Layer {
this.tileList = {};
this.type = this.get('layerType');
this.workerPool = this.scene.workerPool;
- this.workerTileSource = new TileWorkerSource({
- workerPool: this.scene.workerPool
- });
}
shape(field, values) {
const layerType = this.get('layerType');
diff --git a/src/source/source_cache.js b/src/source/source_cache.js
new file mode 100644
index 0000000000..593e126745
--- /dev/null
+++ b/src/source/source_cache.js
@@ -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];
+ // 移除对应的数据
+ }
+ }
+
+}
diff --git a/src/source/tile_worker_source.js b/src/source/tile_worker_source.js
deleted file mode 100644
index dc314d1e17..0000000000
--- a/src/source/tile_worker_source.js
+++ /dev/null
@@ -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')
- });
- }
-}
diff --git a/src/source/vector_tile_source.js b/src/source/vector_tile_source.js
new file mode 100644
index 0000000000..1c124cf8d8
--- /dev/null
+++ b/src/source/vector_tile_source.js
@@ -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);
+ }
+}
diff --git a/src/source/vector_tile_worker_source.js b/src/source/vector_tile_worker_source.js
new file mode 100644
index 0000000000..d96487b150
--- /dev/null
+++ b/src/source/vector_tile_worker_source.js
@@ -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];
+ });
+ }
+}
diff --git a/src/worker/actor.js b/src/worker/actor.js
new file mode 100644
index 0000000000..e39a9eccef
--- /dev/null
+++ b/src/worker/actor.js
@@ -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: '',
+ 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: '',
+ 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 === '' || data.type === '') {
+ this.callbacks[id](id);
+ delete this.callbacks[id]; // 回调执行
+
+ }
+ }
+}
diff --git a/src/worker/worker.js b/src/worker/worker.js
index 1a5f69cfe3..b5918345d9 100644
--- a/src/worker/worker.js
+++ b/src/worker/worker.js
@@ -1,45 +1,60 @@
-import Source from '../core/source';
-// import Controller from '../core/controller/index';
-import { getArrayBuffer } from '../util/ajax';
+import VectorTileWorkerSource from '../source/vector_tile_worker_source';
+import Actor from './actor';
+
+
+// 统一管理workerSource 实例化
export default class Worker {
constructor(self) {
this.self = self;
- this.self.addEventListener('message', cfg => {
- this.loadTile(cfg.data);
- });
+ this.actor = new Actor(self, this);
+ 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) {
- // 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) {
- const tileData = new Source({
- ...cfg.sourceCfg,
- data
- });
- return tileData;
+ setLayers(mapId, layercfgs,callback) {
+
+ }
+ updateLayers(id, params, callback) {
+
+ }
+ /**
+ * 获取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);
diff --git a/src/worker/workerTile.js b/src/worker/workerTile.js
new file mode 100644
index 0000000000..f24473251f
--- /dev/null
+++ b/src/worker/workerTile.js
@@ -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';
+ }
+}
diff --git a/src/worker/worker_controller.js b/src/worker/worker_controller.js
new file mode 100644
index 0000000000..f18f01b67a
--- /dev/null
+++ b/src/worker/worker_controller.js
@@ -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;
+
diff --git a/src/worker/worker_pool.1.js b/src/worker/worker_pool.1.js
new file mode 100644
index 0000000000..b051f02749
--- /dev/null
+++ b/src/worker/worker_pool.1.js
@@ -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();
+ });
+ }
+}
+
diff --git a/src/worker/worker_pool.js b/src/worker/worker_pool.js
index b051f02749..078947cef7 100644
--- a/src/worker/worker_pool.js
+++ b/src/worker/worker_pool.js
@@ -1,54 +1,38 @@
import WebWorker from './web_worker';
+
+/**
+ * Constructs a worker pool.
+ * @private
+ */
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(); // 初始化线程池
+
+ constructor() {
+ this.active = {};
}
- _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
- });
+
+ acquire(mapId) {
+ if (!this.workers) {
+ // Lazily look up the value of mapboxgl.workerCount so that
+ // client code has had a chance to set it.
+ this.workers = [];
+ while (this.workers.length < WorkerPool.workerCount) {
+ this.workers.push(new WebWorker());
}
- });
+ }
+
+ this.active[mapId] = true;
+ return this.workers.slice();
}
- release() {
- this.workers.forEach(worker => {
- worker.terminate();
- });
+
+ release(mapId) {
+ 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);
diff --git a/src/worker/worker_transform.js b/src/worker/worker_transform.js
new file mode 100644
index 0000000000..7fbfc26bb8
--- /dev/null
+++ b/src/worker/worker_transform.js
@@ -0,0 +1,6 @@
+export function serialize() {
+}
+
+export function deserialize() {
+
+}