feat(tile): fix point tile layer

This commit is contained in:
thinkinggis 2019-05-27 10:55:28 +08:00
parent 44d405ce93
commit b6d2109a68
17 changed files with 390 additions and 127 deletions

View File

@ -47,6 +47,7 @@ scene.on('loaded', () => {
} }
) )
.source(city) .source(city)
.active(false)
.color('pm2_5_24h',["#FFF5B8","#FFDC7D","#FFAB5C","#F27049","#D42F31","#730D1C"]) .color('pm2_5_24h',["#FFF5B8","#FFDC7D","#FFAB5C","#F27049","#D42F31","#730D1C"])
.shape('fill') .shape('fill')
.style({ .style({

View File

@ -25,39 +25,58 @@
const scene = new L7.Scene({ const scene = new L7.Scene({
id: 'map', id: 'map',
mapStyle: 'light', // 样式URL mapStyle: 'dark', // 样式URL
center: [120.05859375,30.29701788337204 ], center: [116.5909,39.9225 ],
pitch: 0, pitch: 0,
hash:true, hash:true,
zoom: 11, zoom: 14,
}); });
window.scene = scene; window.scene = scene;
scene.on('loaded', () => { scene.on('loaded', () => {
const layer = scene.VectorTileLayer({ const layer = scene.VectorTileLayer({
zIndex:0, zIndex:0,
layerType:'polygon' layerType:'point'
}) })
//.source('https://pre-lbs-show.taobao.com/gettile?x={x}&y={y}&z={z}&pipeId=pipe_vt_test') //.source('https://pre-lbs-show.taobao.com/gettile?x={x}&y={y}&z={z}&pipeId=pipe_vt_test')
// http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point/{z}/{x}/{y}.pbf
// https://mvt.amap.com/district/CHN2/8/203/105/4096?key= // https://mvt.amap.com/district/CHN2/8/203/105/4096?key=
.source('http://localhost:5000/test.mbtile/{z}/{x}/{y}.pbf',{ .source('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/point2/{z}/{x}/{y}.pbf',{
parser:{ parser:{
type: 'mvt', type: 'mvt',
sourceLayer:'county4326', sourceLayer:'layer',
idField:'OBJECTID', //idField:'OBJECTID',
maxZoom: 10, maxZoom: 17,
} }
}) })
.shape('line') .scale({
.active({fill:'red'}) total:{
.color('red') min:0,
.render(); max:1000000,
layer.on('click',(feature)=>{ type:'log'
console.log(feature); }
}) })
// cylinder
.shape('hexagon')
.size(2)
.active(false)
.color('total', ['#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd'].reverse())
//.color('#0D408C')
.style({
opacity:1.0
})
.render(
);
//layer.on('mousemove',(feature)=>{
// console.log(feature);
// })
console.log(layer); console.log(layer);
}); });
//OBJECTID',(id)=>{
// const index = id % 8;
//return ['#9e0142','#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd','#5e4fa2'][index];
//}
</script> </script>
</body> </body>
</html> </html>

View File

@ -71,9 +71,9 @@ class Picking {
}); });
} }
_updateRender() { // _updateRender() {
this._renderer.render(this._pickingScene, this._camera, this._pickingTexture); // this._renderer.render(this._pickingScene, this._camera, this._pickingTexture);
} // }
_pick(point, normalisedPoint, layerId) { _pick(point, normalisedPoint, layerId) {
this._update(point); this._update(point);

View File

@ -168,6 +168,7 @@ export default class Layer extends Base {
} else { } else {
scaleDefs[field] = cfg; scaleDefs[field] = cfg;
} }
console.log(options);
return this; return this;
} }
shape(field, values) { shape(field, values) {
@ -373,11 +374,14 @@ export default class Layer extends Base {
setActive(id, color) { setActive(id, color) {
this._activeIds = id; this._activeIds = id;
this.layerMesh.material.setUniformsValue('u_activeId', id);
if (!Array.isArray(color)) { if (!Array.isArray(color)) {
color = ColorUtil.color2RGBA(color); color = ColorUtil.color2RGBA(color);
} }
updateObjecteUniform(this._object3D, { u_activeColor: color }); updateObjecteUniform(this._object3D, {
u_activeColor: color,
u_activeId: id
});
this.scene._engine.update();
} }
_addActiveFeature(e) { _addActiveFeature(e) {
@ -623,23 +627,31 @@ export default class Layer extends Base {
} }
this._activeIds = featureId; this._activeIds = featureId;
// TODO 瓦片图层获取选中数据信息 // TODO 瓦片图层获取选中数据信息
// const feature = this.layerSource.getSelectFeature(featureId); const { feature, style } = this.getSelectFeature(featureId);
const lnglat = this.scene.containerToLngLat(point2d); const lnglat = this.scene.containerToLngLat(point2d);
// const style = this.layerData[featureId - 1]; // const style = this.layerData[featureId - 1];
const target = { const target = {
featureId, featureId,
// feature, feature,
// style, style,
pixel: point2d, pixel: point2d,
type, type,
lnglat: { lng: lnglat.lng, lat: lnglat.lat } lnglat: { lng: lnglat.lng, lat: lnglat.lat }
}; };
if (featureId >= 0 || this._activeIds !== null) { // 拾取到元素,或者离开元素 if (featureId >= 0 || this._activeIds >= 0) { // 拾取到元素,或者离开元素
this.emit(type, target); this.emit(type, target);
} }
}); });
} }
getSelectFeature(featureId) {
const feature = this.layerSource.getSelectFeature(featureId);
const style = this.layerData[featureId - 1];
return {
feature,
style
};
}
/** /**
* 用于过滤数据 * 用于过滤数据
* @param {*} object 更新颜色和数据过滤 * @param {*} object 更新颜色和数据过滤

View File

@ -25,8 +25,8 @@ export default class Scene extends Base {
_initEngine(mapContainer) { _initEngine(mapContainer) {
this._engine = new Engine(mapContainer, this); this._engine = new Engine(mapContainer, this);
this.registerMapEvent(); // this.registerMapEvent();
// this._engine.run(); this._engine.run();
// this.workerPool = new WorkerPool(); // this.workerPool = new WorkerPool();
compileBuiltinModules(); compileBuiltinModules();
} }

View File

@ -56,7 +56,9 @@ export default class Source extends Base {
const { type = 'geojson' } = parser; const { type = 'geojson' } = parser;
const data = this.get('data'); const data = this.get('data');
this.originData = getParser(type)(data, parser); this.originData = getParser(type)(data, parser);
this.data = clone(this.originData); this.data = {
dataArray: clone(this.originData.dataArray)
};
this.data.extent = extent(this.data.dataArray); this.data.extent = extent(this.data.dataArray);
} }
/** /**

View File

@ -13,7 +13,5 @@ export default function NormalBuffer(layerData) {
attributes.sizes.push(size); attributes.sizes.push(size);
}); });
return attributes; return attributes;
} }

View File

@ -20,11 +20,11 @@ const vec3 halo = vec3( 1.0 );
void main() { void main() {
// 纹理坐标 // 纹理坐标
#ifdef TEXCOORD_0 #ifdef TEXCOORD_0
vec2 pos = v_uv + gl_PointCoord / 512.0 * 64.0; vec2 pos = v_uv + gl_PointCoord / 512.0 * 64.0;
pos.y = 1.0 - pos.y; pos.y = 1.0 - pos.y;
vec4 textureColor = texture2D(u_texture, pos); vec4 textureColor = texture2D(u_texture, pos);
gl_FragColor =textureColor; gl_FragColor =textureColor;
return; return;
#endif #endif
if(v_color.a == 0.) if(v_color.a == 0.)
discard; discard;

View File

@ -16,7 +16,7 @@ void main() {
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix; mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
v_color = a_color; v_color = a_color;
v_color.a *= u_opacity; v_color.a *= u_opacity;
if(pickingId == u_activeId) { if(pickingId == u_activeId) {
v_color = u_activeColor; v_color = u_activeColor;
} }
gl_Position = matModelViewProjection * vec4(position, 1.0); gl_Position = matModelViewProjection * vec4(position, 1.0);

View File

@ -22,7 +22,8 @@ export default function DrawFill(layerData, layer) {
geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.a_size, 3)); geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.a_size, 3));
const material = new PolygonMaterial({ const material = new PolygonMaterial({
u_opacity: style.opacity, u_opacity: style.opacity,
u_activeColor: activeOption.fill u_activeColor: activeOption.fill,
u_zoom: layer.scene.getZoom()
}, { }, {
SHAPE: true SHAPE: true
}); });

View File

@ -8,7 +8,7 @@ export default function DrawImage(layerData, layer) {
const { strokeWidth, stroke, opacity } = style; const { strokeWidth, stroke, opacity } = style;
const texture = layer.scene.image.texture; const texture = layer.scene.image.texture;
const attributes = PointBuffer.ImageBuffer(layerData, { const attributes = PointBuffer.ImageBuffer(layerData, {
imagePos: this.scene.image.imagePos imagePos: layer.scene.image.imagePos
}); });
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3)); geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4)); geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));

View File

@ -2,6 +2,7 @@
import Tile from './tile'; import Tile from './tile';
import ImageBuffer from '../../geom/buffer/image'; import ImageBuffer from '../../geom/buffer/image';
import DrawImage from '../render/image/drawImage'; import DrawImage from '../render/image/drawImage';
import * as THREE from '../../core/three';
export default class ImageTile extends Tile { export default class ImageTile extends Tile {
requestTileAsync() { requestTileAsync() {
// Making this asynchronous really speeds up the LOD framerate // Making this asynchronous really speeds up the LOD framerate
@ -13,30 +14,34 @@ export default class ImageTile extends Tile {
}, 0); }, 0);
} }
_requestTile() { _requestTile() {
const urlParams = { const image = this._createDebugMesh();
x: this._tile[0], this._createMesh(image);
y: this._tile[1], this.emit('tileLoaded');
z: this._tile[2] // return;
}; // const urlParams = {
// x: this._tile[0],
// y: this._tile[1],
// z: this._tile[2]
// };
const url = this._getTileURL(urlParams); // const url = this._getTileURL(urlParams);
const image = document.createElement('img'); // const image = document.createElement('img');
image.addEventListener('load', () => { // image.addEventListener('load', () => {
this._isLoaded = true; // this._isLoaded = true;
this._createMesh(image); // this._createMesh(image);
this._ready = true; // this._ready = true;
}, false); // }, false);
// image.addEventListener('progress', event => {}, false); // // image.addEventListener('progress', event => {}, false);
// image.addEventListener('error', event => {}, false); // // image.addEventListener('error', event => {}, false);
image.crossOrigin = ''; // image.crossOrigin = '';
// Load image // // Load image
image.src = url; // image.src = url;
this._image = image; // this._image = image;
} }
_getBufferData(images) { _getBufferData(images) {
const NW = this._tileBounds.getTopLeft(); const NW = this._tileBounds.getTopLeft();
@ -61,6 +66,18 @@ export default class ImageTile extends Tile {
this._object3D.add(mesh); this._object3D.add(mesh);
return this._object3D; return this._object3D;
} }
_createDebugMesh() {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.width = 256;
canvas.height = 256;
context.font = 'Bold 20px Helvetica Neue, Verdana, Arial';
context.fillStyle = '#ff0000';
context.fillText(this._tile.join('/'), 20, 20);
context.rect(0, 0, 256, 256);
context.stroke();
return canvas;
}
_abortRequest() { _abortRequest() {
if (!this._image) { if (!this._image) {
return; return;
@ -68,7 +85,9 @@ export default class ImageTile extends Tile {
this._image.src = ''; this._image.src = '';
} }
getSelectFeature() {
return {};
}
destroy() { destroy() {
// Cancel any pending requests // Cancel any pending requests
this._abortRequest(); this._abortRequest();

View File

@ -37,10 +37,10 @@ export default class Tile extends Base {
this._object3D.onBeforeRender = () => { this._object3D.onBeforeRender = () => {
}; };
this._isLoaded = false; this._isLoaded = false;
this._initControllers();
this.requestTileAsync(data => this._init(data)); this.requestTileAsync(data => this._init(data));
} }
_init(data) { _init(data) {
this._initControllers();
this._creatSource(data); this._creatSource(data);
this._initTileAttrs(); this._initTileAttrs();
this._mapping(); this._mapping();

View File

@ -1,15 +1,22 @@
import Layer from '../../core/layer'; import Layer from '../../core/layer';
import source from '../../core/source'; import source from '../../core/source';
import * as THREE from '../../core/three'; import * as THREE from '../../core/three';
import Global from '../../global';
const { pointShape } = Global;
import { updateObjecteUniform } from '../../util/object3d-util';
import TileCache from './tileCache'; import TileCache from './tileCache';
import pickingFragmentShader from '../../core/engine/picking/picking_frag.glsl'; import pickingFragmentShader from '../../core/engine/picking/picking_frag.glsl';
import { throttle, deepMix } from '@antv/util'; import { throttle, deepMix } from '@antv/util';
import { toLngLat } from '@antv/geo-coord'; import { toLngLat, Bounds, Point } from '@antv/geo-coord';
import { wrapNum } from '@antv/geo-coord/lib/util/index';
import { epsg3857 } from '@antv/geo-coord/lib/geo/crs/crs-epsg3857'; import { epsg3857 } from '@antv/geo-coord/lib/geo/crs/crs-epsg3857';
export default class TileLayer extends Layer { export default class TileLayer extends Layer {
constructor(scene, cfg) { constructor(scene, cfg) {
super(scene, cfg); super(scene, {
this._tileCache = new TileCache(50, this._destroyTile); ...cfg,
keepBuffer: 2
});
this._tileCache = new TileCache(100, this._destroyTile);
this._crs = epsg3857; this._crs = epsg3857;
this._tiles = new THREE.Object3D(); this._tiles = new THREE.Object3D();
this._pickTiles = new THREE.Object3D(); this._pickTiles = new THREE.Object3D();
@ -17,7 +24,7 @@ export default class TileLayer extends Layer {
this.scene._engine._picking.add(this._pickTiles); this.scene._engine._picking.add(this._pickTiles);
this._tiles.frustumCulled = false; this._tiles.frustumCulled = false;
this._tileKeys = []; this._tileKeys = [];
this.tileList = []; this.tileList = {};
} }
shape(field, values) { shape(field, values) {
const layerType = this.get('layerType'); const layerType = this.get('layerType');
@ -45,9 +52,9 @@ export default class TileLayer extends Layer {
return new source(tileSourceCfg); return new source(tileSourceCfg);
} }
render() { render() {
this._initControllers(); // this._initControllers();
this._initMapEvent(); this._initMapEvent();
this._initAttrs(); // this._initAttrs();
this._initInteraction(); this._initInteraction();
this.draw(); this.draw();
return this; return this;
@ -56,86 +63,118 @@ export default class TileLayer extends Layer {
this._object3D.add(this._tiles); this._object3D.add(this._tiles);
this._calculateLOD(); this._calculateLOD();
} }
drawTile() { drawTile() {
} }
zoomchange(ev) { zoomchange(ev) {
super.zoomchange(ev); super.zoomchange(ev);
throttle(this._calculateLOD, 200); throttle(this._calculateLOD, 200);
this._calculateLOD(); this._calculateLOD();
} }
dragend(ev) { dragend(ev) {
super.dragend(ev); super.dragend(ev);
this._calculateLOD(); this._calculateLOD();
} }
_calculateLOD() { _calculateLOD() {
const viewPort = this.scene.getBounds().toBounds(); /**
const SE = viewPort.getSouthEast(); * 加载完成 active
const NW = viewPort.getNorthWest(); * 需要显示 current
* 是否保留 retain
*/
this.updateTileList = [];
const zoom = Math.round(this.scene.getZoom()) - 1; const zoom = Math.round(this.scene.getZoom()) - 1;
const tileCount = Math.pow(2, zoom);
const center = this.scene.getCenter(); const center = this.scene.getCenter();
const NWPoint = this._crs.lngLatToPoint(toLngLat(NW.lng, NW.lat), zoom);
const SEPoint = this._crs.lngLatToPoint(toLngLat(SE.lng, SE.lat), zoom);
const centerPoint = this._crs.lngLatToPoint(toLngLat(center.lng, center.lat), zoom); const centerPoint = this._crs.lngLatToPoint(toLngLat(center.lng, center.lat), zoom);
const centerXY = centerPoint.divideBy(256).round(); const centerXY = centerPoint.divideBy(256).round();
const minXY = NWPoint.divideBy(256).round(); const pixelBounds = this._getPixelBounds();
const maxXY = SEPoint.divideBy(256).round(); const tileRange = this._pxBoundsToTileRange(pixelBounds);
// console.log(NW.lng, NW.lat, SE.lng, SE.lat, NWPonint, SEPonint); const margin = this.get('keepBuffer');
let updateTileList = []; this.noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([ margin, -margin ]),
this.tileList = []; tileRange.getTopRight().add([ margin, -margin ]));
const halfx = 1; if (!(isFinite(tileRange.min.x) &&
const halfy = 1; isFinite(tileRange.min.y) &&
isFinite(tileRange.max.x) &&
if (!(centerPoint.x > NWPoint.x && centerPoint.x < SEPoint.x)) { // 地图循环的问题 isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
for (let i = 0; i < minXY.x; i++) { for (let j = tileRange.min.y; j <= tileRange.max.y; j++) {
for (let j = Math.min(0, minXY.y - halfy); j < Math.max(maxXY.y + halfy, tileCount); j++) { for (let i = tileRange.min.x; i <= tileRange.max.x; i++) {
this._updateTileList(updateTileList, i, j, zoom); const coords = [ i, j, zoom ];
} const tile = this.tileList[coords.join('_')];
} if (tile) {
for (let i = maxXY.x; i < tileCount; i++) { tile.current = true;
for (let j = Math.min(0, minXY.y - halfy); j < Math.max(maxXY.y + halfy, tileCount); j++) { } else {
this._updateTileList(updateTileList, i, j, zoom); this.tileList[coords.join('_')] = {
current: true,
coords
};
this.updateTileList.push(coords);
} }
} }
} }
for (let i = Math.max(0, minXY.x - halfx); i < Math.min(maxXY.x + halfx, tileCount); i++) { this.updateTileList.sort((a, b) => {
for (let j = Math.max(0, minXY.y - halfy); j < Math.min(maxXY.y + halfy, tileCount); j++) { const tile1 = a;
this._updateTileList(updateTileList, i, j, zoom); const tile2 = b;
}
}
// 过滤掉已经存在的
// tileList = tileList.filter(tile => {
// })
updateTileList = updateTileList.sort((a, b) => {
const tile1 = a.split('_');
const tile2 = b.split('_');
const d1 = Math.pow((tile1[0] * 1 - centerXY.x), 2) + Math.pow((tile1[1] * 1 - centerXY.y), 2); 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); const d2 = Math.pow((tile2[0] * 1 - centerXY.x), 2) + Math.pow((tile2[1] * 1 - centerXY.y), 2);
return d1 - d2; return d1 - d2;
}); });
updateTileList.forEach(key => { this._pruneTiles();
this._requestTile(key, this); // 更新瓦片数据
this.updateTileList.forEach(coords => {
const key = coords.join('_');
if (this.tileList[key].current) {
this._requestTile(key, this);
}
}); });
this._removeOutTiles();
} }
_getShape(layerData) {
_updateTileList(updateTileList, x, y, z) { let shape = null;
if (!layerData[0].hasOwnProperty('shape')) {
return 'normal';
}
for (let i = 0; i < layerData.length; i++) {
shape = layerData[i].shape;
if (shape !== undefined) {
break;
}
}
if (
pointShape['2d'].indexOf(shape) !== -1 ||
pointShape['3d'].indexOf(shape) !== -1
) {
return 'fill';
} else if (this.scene.image.imagesIds.indexOf(shape) !== -1) {
return 'image';
}
return 'text';
}
_updateTileList(x, y, z) {
const key = [ x, y, z ].join('_'); const key = [ x, y, z ].join('_');
this.tileList.push(key); const tile = this.tileList[key];
if (this._tileKeys.indexOf(key) === -1 && updateTileList.indexOf(key) === -1) { if (tile) {
updateTileList.push(key); tile.current = true;
} else {
this.tileList[key] = {
current: true,
active: false,
coords: key.split('_')
};
this.updateTileList.push(key);
} }
} }
_requestTile(key, layer) { _requestTile(key, layer) {
const t = this.tileList[key];
if (!t) {
return;
}
let tile = this._tileCache.getTile(key); let tile = this._tileCache.getTile(key);
if (!tile) { if (!tile) {
tile = this._createTile(key, layer); tile = this._createTile(key, layer);
tile.on('tileLoaded', () => { tile.on('tileLoaded', () => {
if (this.tileList.indexOf(key) === -1) { t.active = true;
return;
}
const mesh = tile.getMesh(); const mesh = tile.getMesh();
mesh.name = key; mesh.name = key;
this._tileCache.setTile(tile, key); this._tileCache.setTile(tile, key);
@ -144,12 +183,16 @@ export default class TileLayer extends Layer {
this._tiles.add(tile.getMesh()); this._tiles.add(tile.getMesh());
this._addPickTile(tile.getMesh()); this._addPickTile(tile.getMesh());
} }
this.scene._engine.update();
this._pruneTiles();
}); });
} else { } else {
this._tiles.add(tile.getMesh()); this._tiles.add(tile.getMesh());
t.active = true;
this._addPickTile(tile.getMesh()); this._addPickTile(tile.getMesh());
this._tileKeys.push(key); this._tileKeys.push(key);
this.scene._engine.update(); this.scene._engine.update();
this._pruneTiles();
} }
} }
_addPickTile(meshobj) { _addPickTile(meshobj) {
@ -165,19 +208,104 @@ export default class TileLayer extends Layer {
this._pickTiles.add(pickingMesh); this._pickTiles.add(pickingMesh);
} }
// 移除视野外的tile getSelectFeature(id) {
_removeOutTiles() { let feat = null;
for (let i = this._tiles.children.length - 1; i >= 0; i--) { this._tileKeys.forEach(key => {
const tile = this._tiles.children[i]; const tile = this._tileCache.getTile(key);
const key = tile.name; const feature = tile ? tile.getSelectFeature(id) : null;
if (this.tileList.indexOf(key) === -1) { if (feature !== null) {
const tileObj = this._tileCache.getTile(key); feat = feature;
tileObj && tileObj._abortRequest(); return;
this._tiles.remove(tile); }
});
return { feature: feat };
}
_pruneTiles() {
let tile;
const zoom = Math.round(this.scene.getZoom()) - 1;
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;
}
}
for (const key in this.tileList) {
tile = this.tileList[key];
tile.retain = tile.current;
}
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)) {
this._retainChildren(x, y, z, z + 2);
}
}
}
this._removeOutTiles();
}
_retainParent(x, y, z, minZoom) {
const x2 = Math.floor(x / 2);
const y2 = Math.floor(y / 2);
const z2 = z - 1;
const tile = this.tileList[[ x2, y2, z2 ].join('_')];
if (tile && tile.active) {
tile.retain = true;
return true;
} else if (tile && tile.loaded) {
tile.retain = true;
}
if (z2 > minZoom) {
return this._retainParent(x2, y2, z2, minZoom);
}
return false;
}
_retainChildren(x, y, z, maxZoom) {
for (let i = 2 * x; i < 2 * x + 2; i++) {
for (let j = 2 * y; j < 2 * y + 2; j++) {
const key = [ i, j, z + 1 ].join('_');
const tile = this.tileList[key];
if (tile && tile.active) {
tile.retain = true;
continue;
} else if (tile && tile.loaded) {
tile.retain = true;
}
if (z + 1 < maxZoom) {
this._retainChildren(i, j, z + 1, maxZoom);
}
} }
this._tileKeys = [].concat(this.tileList);
} }
} }
// 移除视野外的tile
_removeOutTiles() {
for (const key in this.tileList) {
if (!this.tileList[key].retain) {
const tileObj = this._tileCache.getTile(key);
if (tileObj) {
tileObj._abortRequest();
this._tiles.remove(tileObj.getMesh());
}
delete this.tileList[key];
}
}
if (this._tiles.children.length > Object.keys(this.tileList).length) {
this._tiles.children.forEach(tile => {
const key = tile.name;
if (!this.tileList[key]) {
this._tiles.remove(tile);
}
});
} // 移除 空的geom
this.scene._engine.update();
}
_removeTiles() { _removeTiles() {
if (!this._tiles || !this._tiles.children) { if (!this._tiles || !this._tiles.children) {
return; return;
@ -187,10 +315,51 @@ export default class TileLayer extends Layer {
this._tiles.remove(this._tiles.children[i]); this._tiles.remove(this._tiles.children[i]);
} }
} }
_getPixelBounds() {
const viewPort = this.scene.getBounds().toBounds();
const NE = viewPort.getNorthEast();
const SW = viewPort.getSouthWest();
const zoom = Math.round(this.scene.getZoom()) - 1;
const center = this.scene.getCenter();
const NEPoint = this._crs.lngLatToPoint(toLngLat(NE.lng, NE.lat), zoom);
const SWPoint = this._crs.lngLatToPoint(toLngLat(SW.lng, SW.lat), zoom);
const centerPoint = this._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 ])
);
}
_wrapCoords(coords) {
const wrapX = [ 0, Math.pow(2, coords[2]) ];
const newX = wrapNum(coords[0], wrapX);
return [ newX, coords[1], coords[2] ];
}
_destroyTile(tile) { _destroyTile(tile) {
tile.destroy(); tile.destroy();
tile = null; tile = null;
} }
desttroy() { destroy() {
} }
} }

View File

@ -1,6 +1,6 @@
import Tile from './tile'; import Tile from './tile';
import { getArrayBuffer } from '../../util/ajax'; import { getArrayBuffer } from '../../util/ajax';
import { destoryObject } from '../../util/object3d-util'; import { destoryObject, updateObjecteUniform } from '../../util/object3d-util';
import * as THREE from '../../core/three'; import * as THREE from '../../core/three';
import MaskMaterial from '../../geom/material/tile/maskMaterial'; import MaskMaterial from '../../geom/material/tile/maskMaterial';
import { getRender } from '../render/index'; import { getRender } from '../render/index';
@ -39,6 +39,9 @@ export default class VectorTile extends Tile {
}); });
} }
_createMesh() { _createMesh() {
if (this.layer.get('layerType') === 'point') {
this.layer.shape = this.layer._getShape(this.layerData);
}
this.mesh = getRender(this.layer.get('layerType'), this.layer.shape)(this.layerData, this.layer); this.mesh = getRender(this.layer.get('layerType'), this.layer.shape)(this.layerData, this.layer);
this.mesh.onBeforeRender = renderer => { this.mesh.onBeforeRender = renderer => {
this._renderMask(renderer); this._renderMask(renderer);
@ -52,6 +55,14 @@ export default class VectorTile extends Tile {
return this._object3D; return this._object3D;
} }
_renderMask(renderer) { _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 maskScene = new THREE.Scene(); const maskScene = new THREE.Scene();
this.maskScene = maskScene; this.maskScene = maskScene;
const tileMesh = this._tileMaskMesh(); const tileMesh = this._tileMaskMesh();
@ -97,6 +108,13 @@ export default class VectorTile extends Tile {
this.xhrRequest.abort(); this.xhrRequest.abort();
} }
getSelectFeature(id) {
const featureIndex = this.source.originData.featureKeys[id];
if (featureIndex) {
return this.source.originData.dataArray[featureIndex];
}
return null;
}
destroy() { destroy() {
super.destroy(); super.destroy();
destoryObject(this.maskScene); destoryObject(this.maskScene);

View File

@ -1,27 +1,35 @@
import * as turfMeta from '@turf/meta'; import * as turfMeta from '@turf/meta';
import { getCoords } from '@turf/invariant'; import { getCoords } from '@turf/invariant';
import { BKDRHash } from '../../util/bkdr-hash';
export default function geoJSON(data, cfg) { export default function geoJSON(data, cfg) {
const resultData = []; const resultData = [];
const featureKeys = {};
data.features = data.features.filter(item => { data.features = data.features.filter(item => {
return item != null && item.geometry && item.geometry.type && item.geometry.coordinates && item.geometry.coordinates.length > 0; return item != null && item.geometry && item.geometry.type && item.geometry.coordinates && item.geometry.coordinates.length > 0;
}); });
// 数据为空时处理 // 数据为空时处理
turfMeta.flattenEach(data, (currentFeature, featureIndex) => { // 多个polygon 拆成一个 turfMeta.flattenEach(data, (currentFeature, featureIndex) => { // 多个polygon 拆成一个
const coord = getCoords(currentFeature); const coord = getCoords(currentFeature);
let id = featureIndex + 1; let id = featureIndex + 1;
if (cfg.idField) { // if (cfg.idField) {
id = currentFeature.properties[cfg.idField]; // const value = currentFeature.properties[cfg.idField];
} // // id = value;
// id = BKDRHash(value) % 1000019;
// if (featureKeys[id] && featureIndex !== featureKeys[id]) {
// // TODO 哈希冲突解决方法
// console.log('哈希冲突', value);
// }
// featureKeys[id] = featureIndex;
// }
const dataItem = { const dataItem = {
...currentFeature.properties, ...currentFeature.properties,
coordinates: coord, coordinates: coord,
_id: id _id: currentFeature.properties[cfg.idField]
}; };
resultData.push(dataItem); resultData.push(dataItem);
}); });
return { return {
dataArray: resultData dataArray: resultData,
featureKeys
}; };
} }

16
src/util/bkdr-hash.js Normal file
View File

@ -0,0 +1,16 @@
export function BKDRHash(str) {
const seed = 131;
const seed2 = 137;
let hash = 0;
// make hash more sensitive for short string like 'a', 'b', 'c'
str += 'x';
// Note: Number.MAX_SAFE_INTEGER equals 9007199254740991
const MAX_SAFE_INTEGER = parseInt(9007199254740991 / seed2);
for (let i = 0; i < str.length; i++) {
if (hash > MAX_SAFE_INTEGER) {
hash = parseInt(hash / seed2);
}
hash = hash * seed + str.charCodeAt(i);
}
return hash;
}