mirror of https://gitee.com/antv-l7/antv-l7
fix(rendermask): maskmesh
This commit is contained in:
parent
36f6e1dfa9
commit
1c2c33ed05
|
@ -51,7 +51,7 @@ scene.on('loaded', () => {
|
|||
maxZoom: 9,
|
||||
}
|
||||
})
|
||||
.shape('line')
|
||||
.shape('fill')
|
||||
.size(2)
|
||||
.active(false)
|
||||
.color('red')
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 ])
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -73,3 +73,4 @@ export default class PointLayer extends Layer {
|
|||
}
|
||||
}
|
||||
}
|
||||
PointLayer.type = 'point';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ export default class LRUCache {
|
|||
if (value) {
|
||||
this._deleteCache(key);
|
||||
this._deleteOrder(key);
|
||||
this.destroy(value);
|
||||
this.destroy(value, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue