fix(rendermask): maskmesh

This commit is contained in:
thinkinggis 2019-07-15 20:45:45 +08:00
parent 36f6e1dfa9
commit 1c2c33ed05
30 changed files with 743 additions and 284 deletions

View File

@ -51,7 +51,7 @@ scene.on('loaded', () => {
maxZoom: 9,
}
})
.shape('line')
.shape('fill')
.size(2)
.active(false)
.color('red')

View File

@ -36,7 +36,7 @@ const scene = new L7.Scene({
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',
type:'vector',
minZoom: 0,
maxZoom:9
})
@ -50,15 +50,40 @@ scene.on('loaded', () => {
idField:'id'
}
})
.shape('line')
.size(2)
.scale({
'OBJECTID':{
min:0,
max:3000,
type:'linear'
}
})
.shape('fill')
.active(false)
.color('red')
.color('OBJECTID',['#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd'])
.style({
opacity:1.0
})
.render();
const layer2 = scene.PolygonLayer({
zIndex:10,
})
.source('test',{
parser:{
type: 'mvt',
sourceLayer:'county',
idField:'id'
}
})
.shape('line')
.size(1)
.active(false)
.color('#fff')
.style({
opacity:1.0
})
.render();
});
</script>

View File

@ -4,16 +4,32 @@
*/
import Util from '../util';
const RGB_REG = /rgba?\(([\s.,0-9]+)\)/;
// const RGBA_REG = /rgba\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(\d+)\s*\)/;
// 创建辅助 tag 取颜色
function createTmp() {
const i = document.createElement('i');
i.title = 'Web Colour Picker';
i.style.display = 'none';
document.body.appendChild(i);
return i;
}
const ColorKeywords = {
aliceblue: '#F0F8FF', antiquewhite: '#FAEBD7', aqua: '#00FFFF', aquamarine: '#7FFFD4', azure: '#F0FFFF',
beige: '#F5F5DC', bisque: '#FFE4C4', black: '#000000', blanchedalmond: '#FFEBCD', blue: '#0000FF', blueviolet: '#8A2BE2',
brown: '#A52A2A', burlywood: '#DEB887', cadetblue: '#5F9EA0', chartreuse: '#7FFF00', chocolate: '#D2691E', coral: '#FF7F50',
cornflowerblue: '#6495ED', cornsilk: '#FFF8DC', crimson: '#DC143C', cyan: '#00FFFF', darkblue: '#00008B', darkcyan: '#008B8B',
darkgoldenrod: '#B8860B', darkgray: '#A9A9A9', darkgreen: '#006400', darkgrey: '#A9A9A9', darkkhaki: '#BDB76B', darkmagenta: '#8B008B',
darkolivegreen: '#556B2F', darkorange: '#FF8C00', darkorchid: '#9932CC', darkred: '#8B0000', darksalmon: '#E9967A', darkseagreen: '#8FBC8F',
darkslateblue: '#483D8B', darkslategray: '#2F4F4F', darkslategrey: '#2F4F4F', darkturquoise: '#00CED1', darkviolet: '#9400D3',
deeppink: '#FF1493', deepskyblue: '#00BFFF', dimgray: '#696969', dimgrey: '#696969', dodgerblue: '#1E90FF', firebrick: '#B22222',
floralwhite: '#FFFAF0', forestgreen: '#228B22', fuchsia: '#FF00FF', gainsboro: '#DCDCDC', ghostwhite: '#F8F8FF', gold: '#FFD700',
goldenrod: '#DAA520', gray: '#808080', green: '#008000', greenyellow: '#ADFF2F', grey: '#808080', honeydew: '#F0FFF0', hotpink: '#FF69B4',
indianred: '#CD5C5C', indigo: '#4B0082', ivory: '#FFFFF0', khaki: '#F0E68C', lavender: '#E6E6FA', lavenderblush: '#FFF0F5', lawngreen: '#7CFC00',
lemonchiffon: '#FFFACD', lightblue: '#ADD8E6', lightcoral: '#F08080', lightcyan: '#E0FFFF', lightgoldenrodyellow: '#FAFAD2', lightgray: '#D3D3D3',
lightgreen: '#90EE90', lightgrey: '#D3D3D3', lightpink: '#FFB6C1', lightsalmon: '#FFA07A', lightseagreen: '#20B2AA', lightskyblue: '#87CEFA',
lightslategray: '#778899', lightslategrey: '#778899', lightsteelblue: '#B0C4DE', lightyellow: '#FFFFE0', lime: '#00FF00', limegreen: '#32CD32',
linen: '#FAF0E6', magenta: '#FF00FF', maroon: '#800000', mediumaquamarine: '#66CDAA', mediumblue: '#0000CD', mediumorchid: '#BA55D3',
mediumpurple: '#9370DB', mediumseagreen: '#3CB371', mediumslateblue: '#7B68EE', mediumspringgreen: '#00FA9A', mediumturquoise: '#48D1CC',
mediumvioletred: '#C71585', midnightblue: '#191970', mintcream: '#F5FFFA', mistyrose: '#FFE4E1', moccasin: '#FFE4B5', navajowhite: '#FFDEAD',
navy: '#000080', oldlace: '#FDF5E6', olive: '#808000', olivedrab: '#6B8E23', orange: '#FFA500', orangered: '#FF4500', orchid: '#DA70D6',
palegoldenrod: '#EEE8AA', palegreen: '#98FB98', paleturquoise: '#AFEEEE', palevioletred: '#DB7093', papayawhip: '#FFEFD5', peachpuff: '#FFDAB9',
peru: '#CD853F', pink: '#FFC0CB', plum: '#DDA0DD', powderblue: '#B0E0E6', purple: '#800080', rebeccapurple: '#663399', red: '#FF0000', rosybrown: '#BC8F8F',
royalblue: '#4169E1', saddlebrown: '#8B4513', salmon: '#FA8072', sandybrown: '#F4A460', seagreen: '#2E8B57', seashell: '#FFF5EE',
sienna: '#A0522D', silver: '#C0C0C0', skyblue: '#87CEEB', slateblue: '#6A5ACD', slategray: '#708090', slategrey: '#708090', snow: '#FFFAFA',
springgreen: '#00FF7F', steelblue: '#4682B4', tan: '#D2B48C', teal: '#008080', thistle: '#D8BFD8', tomato: '#FF6347', turquoise: '#40E0D0',
violet: '#EE82EE', wheat: '#F5DEB3', white: '#FFFFFF', whitesmoke: '#F5F5F5', yellow: '#FFFF00', yellowgreen: '#9ACD32'
};
// 获取颜色之间的插值
function getValue(start, end, percent, index) {
@ -36,13 +52,15 @@ function calColor(colors, percent) {
// rgb 颜色转换成数组
function rgb2arr(str) {
const arr = [];
if (str.length === 4) {
str = `#${str[1]}${str[1]}${str[2]}${str[2]}${str[3]}${str[3]}`;
}
arr.push(parseInt(str.substr(1, 2), 16));
arr.push(parseInt(str.substr(3, 2), 16));
arr.push(parseInt(str.substr(5, 2), 16));
return arr;
}
const colorCache = {};
let iEl = null;
const ColorUtil = {
/**
* 将颜色转换到 rgb 的格式
@ -51,33 +69,29 @@ const ColorUtil = {
*/
toRGB(color) {
// 如果已经是 rgb的格式
if (color[0] === '#' && color.length === 7) {
const colorArray = rgb2arr(color);
let colorArray = [ 255, 255, 255, 255 ];
if (ColorKeywords[color]) { // color name 2 hex
const hexColor = ColorKeywords[color];
colorArray = rgb2arr(hexColor);
colorArray.push(255.0);
}
if (color[0] === '#' && (color.length === 7 || color.length === 4)) { // hex2array
colorArray = rgb2arr(color);
colorArray.push(255.0);
return colorArray;
}
if (!iEl) { // 防止防止在页头报错
iEl = createTmp();
}
let rst;
if (colorCache[color]) {
rst = colorCache[color];
} else {
iEl.style.color = color;
rst = document.defaultView.getComputedStyle(iEl, '').getPropertyValue('color');
const matchs = RGB_REG.exec(rst);
const cArray = matchs[1].split(/\s*,\s*/);
if (cArray.length === 4) {
cArray[3] *= 255;
if (RGB_REG.test(color)) {
const matchs = RGB_REG.exec(color);
colorArray = matchs[1].split(/\s*,\s*/);
if (colorArray.length === 4) {
colorArray[3] *= 255;
}
if (cArray.length === 3) {
cArray.push(255.0);
if (colorArray.length === 3) {
colorArray.push(255.0);
}
colorCache[color] = cArray;
rst = cArray;
}
return rst;
colorCache[color] = colorArray;
return colorArray;
},
// 转成 WebGl color buffer
color2Arr(str) {

View File

@ -70,6 +70,7 @@ export default class Layer extends Base {
this._mapEventHandlers = [];
const layerId = this._getUniqueId();
this.set('layerId', layerId);
this.set('mapType', this.scene.mapType);
this.layerId = layerId;
this._activeIds = null;
const world = scene._engine.world;
@ -77,7 +78,6 @@ export default class Layer extends Base {
this.layerMesh = null;
this.layerLineMesh = null;
this._initEvents();
}
/**
* 将图层添加加到 Object
@ -139,6 +139,10 @@ export default class Layer extends Base {
id: data,
...cfg
});
this.scene.style.addLayer(this);
// 初始化tiles
this.tiles = new THREE.Object3D();
this._object3D.add(this.tiles);
return this;
}

View File

@ -27,7 +27,7 @@ export default class Scene extends Base {
_initEngine(mapContainer) {
this._engine = new Engine(mapContainer, this);
// this.registerMapEvent();
this.registerMapEvent();
this._engine.run();
compileBuiltinModules();
}
@ -77,7 +77,12 @@ export default class Scene extends Base {
return this.style.getSource(id);
}
on(type, hander) {
if (this.map) { this.map.on(type, hander); }
if (this.map && type !== 'loaded') {
this.map.on(type, hander);
return;
}
super.on(type, hander);
}
off(type, hander) {

View File

@ -1,5 +1,6 @@
import Base from '../core/base';
import WorkerPool from '../worker/worker_pool';
import { throttle } from '@antv/util';
import { toLngLat, Bounds } from '@antv/geo-coord';
import SourceCache from '../source/source_cache';
import WorkerController from '../worker/worker_controller';
@ -14,19 +15,25 @@ export default class Style extends Base {
this._tileMap = {};
this.WorkerController = new WorkerController(this.WorkerPool, this);
this.layerStyles = {};
this.layers = [];
this.addMapEvent();
}
addSource(id, sourceCfg) {
if (this._sourceCaches[id] !== undefined) {
throw new Error('SourceID 已存在');
}
sourceCfg.sourceID = id;
this._sourceCaches[id] = new SourceCache(this.scene, sourceCfg);
}
getSource(id) {
return this._sourceCaches[id];
}
addLayer(layer) {
const id = layer.layerId;
this.layers[id] = layer;
}
// 设置
addTileStyle(layerCfg) {
_addTileStyle(layerCfg) {
const layerid = layerCfg.layerId;
this.layerStyles[layerid] = layerCfg;
this._layerStyleGroupBySourceID();
@ -52,80 +59,27 @@ export default class Style extends Base {
this.sourceStyles = sourceStyles;
}
update(parameters) {
this.addTileStyle(parameters);
this._addTileStyle(parameters);
for (const key in this._sourceCaches) {
this._sourceCaches[key].update(this.sourceStyles[key]);
this._sourceCaches[key].update(this.layers, this.sourceStyles[key]);
}
}
addMapEvent() {
this.mapEventHander = () => {
for (const key in this._sourceCaches) {
this._sourceCaches[key].update(this.sourceStyles[key]);
}
requestAnimationFrame(() => {
for (const key in this._sourceCaches) {
this._sourceCaches[key].update(this.layers, this.sourceStyles[key]);
}
});
};
this.scene.on('zoomchange', this.mapEventHander);
this.scene.on('dragend', this.mapEventHander);
this.scene.map.on('zoomchange', this.mapEventHander);
this.scene.map.on('dragend', this.mapEventHander);
}
clearMapEvent() {
this.scene.off('zoomchange', this.mapEventHander);
this.scene.off('dragend', this.mapEventHander);
this.scene.map.off('zoomchange', this.mapEventHander);
this.scene.map.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 ])
);
}
}

View File

@ -1,5 +1,7 @@
import Base from '../../core/base';
import * as THREE from '../../core/three';
// import * as from '../../core/three';
import { Vector3 } from 'three/src/math/Vector3';
import { Texture } from 'three/src/textures/Texture';
import { faceNormals } from '../normals';
import extrude from '../extrude';
@ -69,12 +71,12 @@ export default class BufferBase extends Base {
const normals = new Float32Array(indexCount * 3);
const colors = new Float32Array(indexCount * 4);
const pickingIds = new Float32Array(indexCount);
const pA = new THREE.Vector3();
const pB = new THREE.Vector3();
const pC = new THREE.Vector3();
const pA = new Vector3();
const pB = new Vector3();
const pC = new Vector3();
const cb = new THREE.Vector3();
const ab = new THREE.Vector3();
const cb = new Vector3();
const ab = new Vector3();
let lastIndex = 0;
indices.forEach((indice, pIndex) => {
for (let i = 0; i < indice.length / 3; i++) {
@ -176,12 +178,12 @@ export default class BufferBase extends Base {
const normals = new Float32Array(indexCount * 3);
const colors = new Float32Array(indexCount * 4);
const pickingIds = new Float32Array(indexCount);
const pA = new THREE.Vector3();
const pB = new THREE.Vector3();
const pC = new THREE.Vector3();
const pA = new Vector3();
const pB = new Vector3();
const pC = new Vector3();
const cb = new THREE.Vector3();
const ab = new THREE.Vector3();
const cb = new Vector3();
const ab = new Vector3();
let lastIndex = 0;
indices.forEach((indice, pIndex) => {
for (let i = 0; i < indice.length / 3; i++) {
@ -395,7 +397,7 @@ export default class BufferBase extends Base {
// then draw the image
context2.drawImage(canvas, 0, 0, canvas2.width, canvas2.height);
// return the just built canvas2
const texture = new THREE.Texture(canvas2);
const texture = new Texture(canvas2);
// texture.anisotropy = renderer.getMaxAnisotropy();
texture.needsUpdate = true;

View File

@ -1,5 +1,10 @@
export { default as PolygonBuffer } from './polygon';
export { default as PointBuffer } from './point';
export { default as LineBuffer } from './line';
export { default as polygonLineBuffer } from './polygon-line';
import PolygonBuffer from './polygon';
import LineBuffer from './line';
// export { default as textBuffer } from './textBuffer';
import { registerBuffer, getBuffer } from './factory';
registerBuffer('polygon', 'fill', PolygonBuffer);
registerBuffer('polygon', 'extrude', PolygonBuffer);
registerBuffer('polygon', 'line', PolygonBuffer);
registerBuffer('line', 'line', LineBuffer);
export { getBuffer };

View File

@ -1,7 +1,6 @@
import { polygonShape } from '../shape';
import BufferBase from './bufferBase';
export default class PolygonBuffer extends BufferBase {
geometryBuffer() {
const layerData = this.get('layerData');
const shape = this.get('shape');
@ -52,7 +51,6 @@ export default class PolygonBuffer extends BufferBase {
this.bufferStruct.sizes = sizes;
if (shape !== 'line') {
this.attributes = this._toPolygonAttributes(this.bufferStruct);
this.faceTexture = this._generateTexture();
} else {
this.attributes = this._toPolygonLineAttributes(this.bufferStruct);

View File

@ -1,5 +1,5 @@
import Interaction from './base';
import { throttle } from '@antv/util';
import throttle from '../util/throttle';
export default class Hash extends Interaction {
constructor(cfg) {
super({
@ -7,7 +7,7 @@ export default class Hash extends Interaction {
...cfg
});
window.addEventListener('hashchange', this._onHashChange.bind(this), false);
this._updateHash = throttle(this._updateHashUnthrottled.bind(this), 20 * 1000 / 100);
this._updateHash = throttle(this._updateHashUnthrottled.bind(this), 30 * 1000 / 100);
}
end() {
this._updateHash();

View File

@ -21,3 +21,4 @@ export default class LineLayer extends Layer {
this.add(getRender('line', this.shapeType || 'line')(this.layerData, this, this.layerSource));
}
}
LineLayer.type = 'line';

View File

@ -73,3 +73,4 @@ export default class PointLayer extends Layer {
}
}
}
PointLayer.type = 'point';

View File

@ -1,8 +1,14 @@
import Layer from '../core/layer';
import { getRender } from './render';
export default class PolygonLayer extends Layer {
constructor(scene, cfg) {
super(scene, cfg);
this.set('type', 'polygon');
}
shape(type) {
this.shape = type;
this.set('shape', type);
this.set('shapeType', 'polygon');
return this;
}
draw() {
@ -18,3 +24,4 @@ export default class PolygonLayer extends Layer {
this.updateFilter(this.layerMesh);
}
}
PolygonLayer.type = 'polygon';

View File

@ -1,5 +1,5 @@
import * as THREE from '../../../core/three';
import { LineBuffer } from '../../../geom/buffer/index';
import LineBuffer from '../../../geom/buffer/line';
import { ArcLineMaterial } from '../../../geom/material/lineMaterial';
export default function DrawArcLine(layerdata, layer) {
const style = this.get('styleOptions');

View File

@ -1,5 +1,5 @@
import * as THREE from '../../../core/three';
import { LineBuffer } from '../../../geom/buffer/index';
import LineBuffer from '../../../geom/buffer/line';
import { MeshLineMaterial } from '../../../geom/material/lineMaterial';
export default function DrawLine(layerData, layer) {

View File

@ -3,7 +3,7 @@ import PolygonBuffer from '../../../geom/buffer/polygon';
import PolygonMaterial from '../../../geom/material/polygonMaterial';
import { generateLightingUniforms } from '../../../util/shaderModule';
export default function DrawPolygonFill(layerData, layer) {
export default function DrawPolygonFill(layerData, layer, buffer) {
const style = layer.get('styleOptions');
const activeOption = layer.get('activedOptions');
const config = {
@ -11,10 +11,13 @@ export default function DrawPolygonFill(layerData, layer) {
activeColor: activeOption.fill
};
const { opacity, activeColor, lights } = config;
const { attributes } = new PolygonBuffer({
shape: layer.shape,
layerData
});
let attributes = buffer;
if (!attributes) {
attributes = new PolygonBuffer({
shape: layer.shape,
layerData
}).attributes;
}
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));

View File

@ -1,7 +1,7 @@
import * as THREE from '../../../core/three';
import PolygonBuffer from '../../../geom/buffer/polygon';
import { LineMaterial } from '../../../geom/material/lineMaterial';
export default function DrawPolygonLine(layerData, layer) {
export default function DrawPolygonLine(layerData, layer, buffer) {
const style = layer.get('styleOptions');
const activeOption = layer.get('activedOptions');
const config = {
@ -9,10 +9,13 @@ export default function DrawPolygonLine(layerData, layer) {
activeColor: activeOption.fill
};
const { opacity } = config;
const { attributes } = new PolygonBuffer({
shape: layer.shape,
layerData
});
let attributes = buffer;
if (!attributes) {
attributes = new PolygonBuffer({
shape: layer.shape,
layerData
}).attributes;
}
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));

View File

@ -27,6 +27,7 @@ export default class Tile extends Base {
this._object3D.onBeforeRender = () => {
};
this._isLoaded = false;
console.time(this._tile);
this.requestTileAsync(data => this._init(data));
}
_init(data) {
@ -40,17 +41,13 @@ export default class Tile extends Base {
this.isValid = true;
this._initControllers();
this._createMesh();
console.timeEnd(this._tile);
}
repaint() {
this._initControllers();
this._createMesh();
}
requestTileAsync(done) {
// 获取数据
// 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);

View File

@ -46,7 +46,7 @@ export default class TileLayer extends Layer {
...cfg,
url: data
};
this.workerTileSource.set('sourceCfg', this.sourceCfg);
}
return this;
}
@ -411,7 +411,6 @@ export default class TileLayer extends Layer {
const nextAttrs = this.get('attrOptions');
const preStyle = this.get('preStyleOption');
const nextStyle = this.get('styleOptions');
this.workerTileSource.set('attrs', nextAttrs);
if (preAttrs === undefined && preStyle === undefined) { // 首次渲染
// this._setPreOption();
this._scaleByZoom();

View File

@ -0,0 +1,133 @@
import { destoryObject, updateObjecteUniform } from '../../util/object3d-util';
import * as THREE from '../../core/three';
import MaskMaterial from '../../geom/material/tile/maskMaterial';
import { toLngLatBounds, toBounds } from '@antv/geo-coord';
import { getRender } from '../render/index';
const r2d = 180 / Math.PI;
export default class VectorTileMesh {
constructor(layer, data) {
this.layer = layer;
this._object3D = new THREE.Object3D();
this._object3D.name = data.tileId;
this._tile = data.tileId.split('_').map(v => v * 1);
this._tileLnglatBounds = this._tileLnglatBounds(this._tile);
this._tileBounds = this._tileBounds(this._tileLnglatBounds);
this._center = this._tileBounds.getCenter();
this._centerLnglat = this._tileLnglatBounds.getCenter();
this._init(data);
this.maskScene = new THREE.Scene();
const tileMesh = this._tileMaskMesh();
this.maskScene.add(tileMesh);
}
_init(data) {
this._createMesh(data);
}
_createMesh(data) {
const layerData = data.layerData;
if (this.layer.get('type') === 'point') {
this.layer.shape = this.layer._getShape(layerData);
}
this.mesh = getRender(this.layer.get('type'), this.layer.shape)(null, this.layer, data.attributes);
if (this.mesh.type !== 'composer') { // 热力图的情况
this.mesh.onBeforeRender = renderer => {
this._renderMask(renderer);
};
this.mesh.onAfterRender = renderer => {
const context = renderer.context;
context.clear(context.STENCIL_BUFFER_BIT);
context.disable(context.STENCIL_TEST);
};
this._object3D.add(this.mesh);
} else { // 如果是热力图
this._object3D = this.mesh;
}
return this._object3D;
}
getMesh() {
return this._object3D;
}
_renderMask(renderer) {
const zoom = this.layer.scene.getZoom();
updateObjecteUniform(this.mesh, {
u_time: this.layer.scene._engine.clock.getElapsedTime(),
u_zoom: zoom
});
if (this.layer.get('layerType') === 'point') { // 点图层目前不需要mask
return;
}
const context = renderer.context;
renderer.autoClear = false;
renderer.clearDepth();
context.enable(context.STENCIL_TEST);
context.stencilOp(context.REPLACE, context.REPLACE, context.REPLACE);
context.stencilFunc(context.ALWAYS, 1, 0xffffffff);
context.clearStencil(0);
context.clear(context.STENCIL_BUFFER_BIT);
context.colorMask(false, false, false, false);
// config the stencil buffer to collect data for testing
this.layer.scene._engine.renderScene(this.maskScene);
context.colorMask(true, true, true, true);
context.depthMask(false);
renderer.clearDepth();
// only render where stencil is set to 1
context.stencilFunc(context.EQUAL, 1, 0xffffffff); // draw if == 1
context.stencilOp(context.KEEP, context.KEEP, context.KEEP);
}
_tileMaskMesh() {
const tilebound = this._tileBounds;
const bl = [ tilebound.getBottomLeft().x, tilebound.getBottomLeft().y, 0 ];
const br = [ tilebound.getBottomRight().x, tilebound.getBottomRight().y, 0 ];
const tl = [ tilebound.getTopLeft().x, tilebound.getTopLeft().y, 0 ];
const tr = [ tilebound.getTopRight().x, tilebound.getTopRight().y, 0 ];
const positions = [ ...bl, ...tr, ...br, ...bl, ...tl, ...tr ];
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
const maskMaterial = new MaskMaterial();
const maskMesh = new THREE.Mesh(geometry, maskMaterial);
return maskMesh;
}
getSelectFeature(id) {
const featurekey = this.layerSource.originData.featureKeys[id];
if (featurekey && featurekey.index !== undefined) {
const featureIndex = featurekey.index;
return this.layerSource.originData.dataArray[featureIndex];
}
return null;
}
_tileBounds(lnglatBound) {
const ne = this.layer.scene.project([ lnglatBound.getNorthEast().lng, lnglatBound.getNorthEast().lat ]);
const sw = this.layer.scene.project([ lnglatBound.getSouthWest().lng, lnglatBound.getSouthWest().lat ]);
return toBounds(sw, ne);
}
// Get tile bounds in WGS84 coordinates
_tileLnglatBounds(tile) {
const e = this._tile2lng(tile[0] + 1, tile[2]);
const w = this._tile2lng(tile[0], tile[2]);
const s = this._tile2lat(tile[1] + 1, tile[2]);
const n = this._tile2lat(tile[1], tile[2]);
return toLngLatBounds([ w, n ], [ e, s ]);
}
_tile2lng(x, z) {
return x / Math.pow(2, z) * 360 - 180;
}
_tile2lat(y, z) {
const n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
}
destroy() {
destoryObject(this._object3D);
destoryObject(this.maskScene);
this._object3D = null;
this.maskScene = null;
this.layerData = null;
}
}

View File

@ -1,7 +1,8 @@
import Base from '../core/base';
import TileDataCache from '../source/tile_data_cache';
import TileCache from '../layer/tile/tile_cache';
import VectorTileSource from './vector_tile_source';
import { toLngLat, Bounds } from '@antv/geo-coord';
import { toLngLat, Bounds, Point } from '@antv/geo-coord';
import VectorTileMesh from '../layer/tile/vector_tile_mesh';
// 统一管理 source 添加,管理,更新
export default class SouceCache extends Base {
constructor(scene, cfg) {
@ -9,46 +10,100 @@ export default class SouceCache extends Base {
cacheLimit: 50,
minZoom: 0,
maxZoom: 18,
keepBuffer: 2,
keepBuffer: 1,
...cfg
});
this._tileMap = {};// 视野内瓦片坐标序列
this._tileList = {}; // 正在使用的瓦片坐标,记录瓦片的使用状态
this.tileList = {}; // 正在使用的瓦片坐标,记录瓦片的使用状态
this.scene = scene;
// TODO 销毁函数
this._tileCache = new TileDataCache(this.get('cacheLimit'), () => { });
this._tileCache = new TileCache(this.get('cacheLimit'), this._destroyTile.bind(this));
this.layers = this.scene.getLayers();
this._source = new VectorTileSource(cfg, this.scene.style.WorkerController);
this.layersTiles = {}; // 存储当前source所有layer的瓦片
// this._tiles = new THREE.Object3D();
}
getLayerById(id) {
const layers = this.scene.getLayers();
for (let i = 0; i < layers.length; i += 1) {
if (layers[i].layerId === id * 1) {
return layers[i];
}
}
}
/**
* 移除视野外的瓦片计算新增的瓦片数据
* @param {*}tileMap 瓦片列表
*/
update(layercfg) {
if (!layercfg && this.layercfg) return;
this._layercfg = layercfg;
update() {
// 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++) {
// this.updateList = this._getNewTiles(this._tileMap);// 计算新增瓦片
// this._pruneTiles();
for (let i = 0; i < this.updateTileList.length; i++) {
// 瓦片相关参数
const tileId = this.updateList[i];
this._source.loadTile(tileId, res => {
this._tileList[tileId].active = true;
const tileId = this.updateTileList[i].join('_');
const tileinfo = this.tileList[tileId];
tileinfo.loading = true;
const tiles = this._tileCache.getTile(tileId);
if (tiles !== undefined) {
tileinfo.active = true;
tileinfo.loaded = true;
for (const layerId in tiles) {
const layer = this.getLayerById(layerId);
const tileMesh = tiles[layerId];
layer.tiles.add(tileMesh.getMesh());
this.scene._engine.update();
}
this._pruneTiles();
continue;
}
this._source.loadTile(tileinfo, (err, data) => {
if (!err && data !== undefined) {
this._renderTile(tileinfo, data);
tileinfo.active = true;
}
tileinfo.loaded = true;
this._pruneTiles();
});
}
}
_renderTile(tileinfo, data) {
const tileId = tileinfo.id;
const tiles = {};
for (const layerId in data) {
const layer = this.getLayerById(layerId);
const tileMesh = new VectorTileMesh(layer, data[layerId]);
tiles[layerId] = tileMesh;
layer.tiles.add(tileMesh.getMesh());
this.scene._engine.update();
}
this._tileCache.setTile(tiles, tileId);
}
// 计算视野内的瓦片坐标
_calculateTileIDs() {
this._tileMap = {};
const zoom = Math.floor(this.scene.getZoom()) - 1;
this.updateTileList = [];
const zoom = Math.floor(this.scene.getZoom()); // zoom - 1
const minSourceZoom = this.get('minZoom');
const maxSourceZoom = this.get('maxZoom');
this.tileZoom = zoom > maxSourceZoom ? maxSourceZoom : zoom;
const currentZoom = this.scene.getZoom();
if (currentZoom < minSourceZoom) {
this._removeTiles();
// 小于source最小范围不在处理
return;
}
const pixelBounds = this._getPixelBounds();
const tileRange = this._pxBoundsToTileRange(pixelBounds);
const margin = this.get('keepBuffer');
const center = this.scene.getCenter();
const centerPoint = this.scene.crs.lngLatToPoint(toLngLat(center.lng, center.lat), this.tileZoom);
const centerXY = centerPoint.divideBy(256).floor();
this._noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([ margin, -margin ]),
tileRange.getTopRight().add([ margin, -margin ]));
if (!(isFinite(tileRange.min.x) &&
@ -58,15 +113,32 @@ export default class SouceCache extends Base {
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 tile = this.tileList[coords.join('_')];
if (tile && tile.loading) {
tile.current = true;
tile.retain = true;
} else {
this.tileList[coords.join('_')] = {
current: true,
active: false,
retain: true,
loading: false,
coords,
id: coords.join('_')
};
this.updateTileList.push(coords);
}
}
}
const currentZoom = this.scene.getZoom();
if (currentZoom < minSourceZoom) {
this._removeTiles();
// 小于source最小范围不在处理
return;
}
// 根据中心点排序
this.updateTileList.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;
});
this._pruneTiles();
}
_getPixelBounds() {
const viewPort = this.scene.getBounds().toBounds();
@ -109,6 +181,17 @@ export default class SouceCache extends Base {
return this._source.loadTile(tile, callback);
}
_unloadTile(tile) {
if (this._source.unloadTile) {
return this._source.unloadTile(tile, () => { });
}
}
_abortTile(tile) {
if (this._source.abortTile) {
return this._source.abortTile(tile, () => { });
}
}
reload() {
}
@ -122,40 +205,18 @@ export default class SouceCache extends Base {
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;
_pruneTiles() {
let tile;
const zoom = this.tileZoom;
for (const key in this.tileList) {
const c = this.tileList[key].coords;
if (c[2] !== zoom || !this._noPruneRange.contains(new Point(c[0], c[1]))) {
this.tileList[key].current = false;
this.tileList[key].retain = false;
}
}
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];
for (const key in this.tileList) {
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)) {
@ -170,7 +231,7 @@ export default class SouceCache extends Base {
const x2 = Math.floor(x / 2);
const y2 = Math.floor(y / 2);
const z2 = z - 1;
const tile = this._tileList[[ x2, y2, z2 ].join('_')];
const tile = this.tileList[[ x2, y2, z2 ].join('_')];
if (tile && tile.active) {
tile.retain = true;
tile.current = true;
@ -189,7 +250,7 @@ export default class SouceCache extends Base {
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];
const tile = this.tileList[key];
if (tile && tile.active) {
tile.retain = true;
tile.current = true;
@ -207,10 +268,32 @@ export default class SouceCache extends Base {
}
_removeOutTiles() {
// 移除视野外的tile
for (const key in this._tileList) {
!this._tileList[key].retain && delete this._tileList[key];
// 移除对应的数据
for (const key in this.tileList) {
if (!this.tileList[key].retain) {
this._abortTile(this.tileList[key]);
this._unloadTile(this.tileList[key]);
delete this.tileList[key];
}
}
const layers = this.scene.getLayers();
for (let i = 0; i < layers.length; i++) {
const id = this.get('sourceID');
const layerSource = layers[i].get('sourceOption').id;
if (layerSource !== id) {
return;
}
layers[i].tiles.children.forEach(tile => {
const key = tile.name;
if (!this.tileList[key]) {
layers[i].tiles.remove(tile);
}
});
}
// 移除对应的数据
}
_destroyTile(tile, key) {
this._unloadTile(key);
}
// 移除视野外的tile
}

View File

@ -1,26 +1,49 @@
import Base from '../core/base';
const tileURLRegex = /\{([zxy])\}/g;
export default class VectorTileSource extends Base {
constructor(cfg, workerController) {
super({
type: 'vector',
...cfg
});
this.cfg = cfg;
this.workerController = workerController;
this.urlTemplate = this.get('url');
}
loadTile(tile, callback) {
loadTile(tileinfo, callback) {
const tileId = tileinfo.id.split('_');
const url = this._getTileURL({
x: tileId[0],
y: tileId[1],
z: tileId[2]
});
const params = {
id: tile
id: tileinfo.id,
type: 'vector',
...this.cfg,
url
};
this.workerController.send('loadTile', params, done.bind(this));
function done(err, data) {
callback();
tileinfo.workerID = this.workerController.send('loadTile', params, done.bind(this));
function done(err, data) { // 收到数据,处理数据
callback(err, data);
}
}
abortTile(tile) {
this.workerController.send('abortTile', { uid: tile.uid, type: this.type, source: this.id }, undefined, tile.workerID);
abortTile(tileinfo) {
this.workerController.send('abortTile', { id: tileinfo.id, type: this.get('type'), sourceID: this.get('sourceID') }, undefined, tileinfo.workerID);
}
unloadTile(tile) {
this.workerController.send('removeTile', { uid: tile.uid, type: this.type, source: this.id }, undefined, tile.workerID);
unloadTile(tileinfo) {
this.workerController.send('removeTile', { id: tileinfo.id, type: this.get('type'), sourceID: this.get('sourceID') }, undefined, tileinfo.workerID);
}
_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];
});
}
}

View File

@ -1,45 +1,106 @@
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();
};
function loadVectorTile(params, callback) {
const request = getArrayBuffer({ url: params.url }, (err, data) => {
if (err) {
callback(err);
} else if (data) {
callback(null, {
vectorTile: new VectorParser.VectorTile(new PBF(data.data)),
rawData: data.data
});
}
});
return () => {
request.abort();
callback();
};
}
export default class VectorTileWorkerSource {
constructor(actor, layerStyle, loadVectorData) {
this.actor = actor;
this.layerStyle = layerStyle;
this.loadVectorData = loadVectorData || loadVectorTile;
this.loaded = {};
this.loading = {};
}
loadTile(params, callback) {
console.log(params);
const workerTile = new WorkerTile(params);
const uid = params.id;
if (!this.loading) {
this.loading = {};
}
const workerTile = this.loading[uid] = new WorkerTile(params);
workerTile.abort = this.loadVectorData(params, (err, response) => {
if (err || !response) {
workerTile.status = 'done';
this.loaded[uid] = workerTile;
return callback(err);
}
// const rawTileData = response.rawData;
workerTile.vectorTile = response.vectorTile;
workerTile.parse(response.vectorTile, this.layerStyle, this.actor, (err, result) => {
if (err || !result) return callback(err);
// Transferring a copy of rawTileData because the worker needs to retain its copy.
callback(null, {
// rawTileData: rawTileData.slice(0),
...result
});
});
this.loaded = this.loaded || {};
this.loaded[uid] = workerTile;
});
}
abortTile() {
abortTile(params, callback) {
const loading = this.loading;
const uid = params.id;
if (loading && loading[uid] && loading[uid].abort) {
loading[uid].abort();
delete loading[uid];
}
callback();
}
reloadTile(params, callback) { // 重新加载 tile
const loaded = this.loaded,
uid = params.id,
vtSource = this;
if (loaded && loaded[uid]) {
const workerTile = loaded[uid];
const done = (err, data) => {
const reloadCallback = workerTile.reloadCallback;
if (reloadCallback) {
delete workerTile.reloadCallback;
workerTile.parse(workerTile.vectorTile, vtSource.layerStyle, vtSource.actor, reloadCallback);
}
callback(err, data);
};
if (workerTile.status === 'parsing') {
workerTile.reloadCallback = done;
} 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) {
workerTile.parse(workerTile.vectorTile, this.layerIndex, this.actor, done);
} else {
done();
}
}
}
}
removeTile(params, callback) {
const loaded = this.loaded,
uid = params.id;
if (loaded && loaded[uid]) {
delete loaded[uid];
}
callback();
}
unloadTile() {
@ -47,15 +108,4 @@ export default class VectorTileSource extends Base {
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];
});
}
}

View File

@ -55,7 +55,7 @@ export default class LRUCache {
if (value) {
this._deleteCache(key);
this._deleteOrder(key);
this.destroy(value);
this.destroy(value, key);
}
}

21
src/util/throttle.js Normal file
View File

@ -0,0 +1,21 @@
export default function throttle(fn, time) {
let pending = false;
let timerId;
const later = () => {
timerId = null;
if (pending) {
fn();
timerId = setTimeout(later, time);
pending = false;
}
};
return () => {
pending = true;
if (!timerId) {
later();
}
return timerId;
};
}

View File

@ -20,14 +20,14 @@ export default class Actor {
send(type, data, callback, targetMapId) {
const id = callback ? `${this.mapId}_${this.callbackID++}` : null;
if (callback) this.callbacks[id] = callback;
const buffers = [];
const buffer = [];
this.target.postMessage({
targetMapId,
sourceMapId: this.mapId,
type,
id: String(id),
data
}, buffers);
}, buffer);
if (callback) {
return {
cancel: () => this.target.postMessage({
@ -40,24 +40,41 @@ export default class Actor {
}
}
receive(message) {
// TODO 处理中断Worker
const data = message.data;
const id = data.id;
if (Object.keys(this.callbacks).length === 0) {
this.target.postMessage({ // worker向主线程发送结果数据
let callback;
const done = (err, data) => {
delete this.callbacks[id];
const buffers = [];
this.target.postMessage({ // 发送结果数据
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 根据类型调用响应的方法
error: err ? JSON.stringify(err) : null,
data: serialize(data, buffers)
}, buffers);
};
if (data.type === '<response>' || data.type === '<cancel>') {
this.callbacks[id](id);
delete this.callbacks[id]; // 回调执行
callback = this.callbacks[data.id];
delete this.callbacks[data.id];
if (callback && data.error) {
callback(data.error);
} else if (callback) {
callback(null, data.data);
}
} else if (typeof data.id !== 'undefined' && this.parent[data.type]) { // loadTile
this.parent[data.type](data.sourceMapId, data.data, done);
} else if (typeof data.id !== 'undefined' && this.parent.getWorkerSource) {
const keys = data.type.split('.');
const params = data.data;
const workerSource = (this.parent).getWorkerSource(data.sourceMapId, keys[0], params.source);
workerSource[keys[1]](params, done);
} else {
this.parent[data.type](data.data);
}
}
}

View File

@ -1,7 +1,5 @@
import VectorTileWorkerSource from '../source/vector_tile_worker_source';
import Actor from './actor';
// 统一管理workerSource 实例化
export default class Worker {
constructor(self) {
@ -17,13 +15,26 @@ export default class Worker {
}
this.workerSourceTypes[name] = WorkerSource;
};
this.layerStyles = {};
}
loadTile(cfg) {
loadTile(mapId, params, callback) {
this.getWorkerSource(mapId, params.type, params.sourceID).loadTile(params, callback);
}
abortTile(mapId, params, callback) {
this.getWorkerSource(mapId, params.type, params.sourceID).abortTile(params, callback);
}
removeTile(mapId, params, callback) {
this.getWorkerSource(mapId, params.type, params.sourceID).removeTile(params, callback);
}
setLayers(mapId, layercfgs, callback) {
this.layerStyles[mapId] = layercfgs; // mapid layerID
if (this.workerSources[mapId]) {
for (const sourceId in this.workerSources[mapId].vector) {
this.workerSources[mapId].vector[sourceId].layerStyle = layercfgs;
}
}
callback();
}
updateLayers(id, params, callback) {
@ -42,7 +53,6 @@ export default class Worker {
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
@ -51,8 +61,7 @@ export default class Worker {
this.actor.send(type, data, callback, mapId);
}
};
this.workerSources[mapId][type][source] = new this.workerSourceTypes[type](actor, this.getLayerIndex(mapId));
this.workerSources[mapId][type][source] = new this.workerSourceTypes[type](actor, this.layerStyles[mapId]);
}
return this.workerSources[mapId][type][source];
}

View File

@ -1,17 +1,72 @@
import TileMapping from '../core/controller/tile_mapping';
import { getBuffer } from '../geom/buffer/index';
import Source from '../core/source';
export default class WorkerTile {
constructor(tile) {
this.id = tile.id;
constructor(params) {
this.tileID = params.id;
this.source = params.sourceID;
this.params = params;
}
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++) {
const sourceStyle = this._layerStyleGroupBySourceID(layerstyle)[this.source];
const tile = this.tileID.split('_');
const sourceLayerData = {};
// 数据源解析
for (const sourcelayer in sourceStyle) { // sourceLayer
const features = [];
const vectorLayer = data.layers[sourcelayer];
if (vectorLayer === undefined) {
return null;
}
for (let i = 0; i < vectorLayer.length; i++) {
const feature = vectorLayer.feature(i);
const geofeature = feature.toGeoJSON(tile[0], tile[1], tile[2]);
features.push(geofeature);
}
const geodata = {
type: 'FeatureCollection',
features
};
for (let i = 0; i < sourceStyle[sourcelayer].length; i++) {
const style = sourceStyle[sourcelayer][i];
style.sourceOption.parser.type = 'geojson';
const tileSource = new Source({
...style.sourceOption,
mapType: style.mapType,
data: geodata
});
const tileMapping = new TileMapping(tileSource, style);
const geometryBuffer = getBuffer(style.type, style.shape);
const buffer = new geometryBuffer({
layerData: tileMapping.layerData,
shape: style.shape
});
sourceLayerData[style.layerId] = {
attributes: buffer.attributes,
// layerData: tileMapping.layerData,
// sourceData: tileSource.data,
layerId: style.layerId,
sourcelayer,
tileId: this.tileID
};
}
}
this.status = 'done';
callback(null, { ...sourceLayerData });
}
_layerStyleGroupBySourceID(layerStyles) {
const sourceStyles = {};
// 支持VectorLayer
for (const layerId in layerStyles) {
const sourceID = layerStyles[layerId].sourceOption.id;
const sourcelayer = layerStyles[layerId].sourceOption.parser.sourceLayer;
if (!sourceStyles[sourceID]) sourceStyles[sourceID] = {};
if (!sourceStyles[sourceID][sourcelayer]) sourceStyles[sourceID][sourcelayer] = [];
sourceStyles[sourceID][sourcelayer].push(layerStyles[layerId]);
}
return sourceStyles;
}
}

View File

@ -48,7 +48,6 @@ export default class WorkerController {
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;

View File

@ -1,4 +1,55 @@
export function serialize() {
export function serialize(input, transferables) {
if (input === null ||
input === undefined ||
typeof input === 'boolean' ||
typeof input === 'number' ||
typeof input === 'string' ||
input instanceof Boolean ||
input instanceof Number ||
input instanceof String ||
input instanceof Date ||
input instanceof RegExp) {
return input;
}
if (input instanceof ArrayBuffer) {
if (transferables) {
transferables.push(input);
}
return input;
}
if (ArrayBuffer.isView(input)) {
const view = input;
if (transferables) {
transferables.push(view.buffer);
}
return view;
}
if (input instanceof ImageData) {
if (transferables) {
transferables.push(input.data.buffer);
}
return input;
}
if (Array.isArray(input)) {
const serialized = [];
for (const item of input) {
serialized.push(serialize(item, transferables));
}
return serialized;
}
if (typeof input === 'object') {
const properties = {};
for (const key in input) {
if (!input.hasOwnProperty(key)) {
continue;
}
const property = input[key];
properties[key] = serialize(property, transferables);
}
return properties;
}
}
export function deserialize() {