From 2bb6db2d7ca8dc0182d879fb9862c3c1b7e6fa21 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Mon, 27 May 2019 10:55:28 +0800 Subject: [PATCH] feat(tile): fix point tile layer --- demos/tile.html | 1 + demos/vectorTile.html | 49 +++-- src/core/engine/picking/picking.js | 6 +- src/core/layer.js | 24 ++- src/core/scene.js | 4 +- src/core/source.js | 4 +- src/geom/buffer/point/normalBuffer.js | 2 - src/geom/shader/point_frag.glsl | 10 +- src/geom/shader/point_vert.glsl | 2 +- src/layer/render/point/drawFill.js | 3 +- src/layer/render/point/drawImage.js | 2 +- src/layer/tile/imageTile.js | 57 +++-- src/layer/tile/tile.js | 2 +- src/layer/tile/tileLayer.js | 293 ++++++++++++++++++++------ src/layer/tile/vectorTile.js | 20 +- src/source/parser/geojson.js | 22 +- src/util/bkdr-hash.js | 16 ++ 17 files changed, 390 insertions(+), 127 deletions(-) create mode 100644 src/util/bkdr-hash.js diff --git a/demos/tile.html b/demos/tile.html index b3ac0aaae2..cd97a2c849 100644 --- a/demos/tile.html +++ b/demos/tile.html @@ -47,6 +47,7 @@ scene.on('loaded', () => { } ) .source(city) + .active(false) .color('pm2_5_24h',["#FFF5B8","#FFDC7D","#FFAB5C","#F27049","#D42F31","#730D1C"]) .shape('fill') .style({ diff --git a/demos/vectorTile.html b/demos/vectorTile.html index f32f7ae714..b420dca765 100644 --- a/demos/vectorTile.html +++ b/demos/vectorTile.html @@ -25,39 +25,58 @@ const scene = new L7.Scene({ id: 'map', - mapStyle: 'light', // 样式URL - center: [120.05859375,30.29701788337204 ], + mapStyle: 'dark', // 样式URL + center: [116.5909,39.9225 ], pitch: 0, hash:true, - zoom: 11, + zoom: 14, }); window.scene = scene; scene.on('loaded', () => { const layer = scene.VectorTileLayer({ zIndex:0, - layerType:'polygon' + layerType:'point' }) //.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= - .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:{ type: 'mvt', - sourceLayer:'county4326', - idField:'OBJECTID', - maxZoom: 10, + sourceLayer:'layer', + //idField:'OBJECTID', + maxZoom: 17, } }) - .shape('line') - .active({fill:'red'}) - .color('red') - .render(); - layer.on('click',(feature)=>{ - console.log(feature); + .scale({ + total:{ + min:0, + max:1000000, + type:'log' + } }) + // 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); }); - +//OBJECTID',(id)=>{ + // const index = id % 8; + //return ['#9e0142','#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd','#5e4fa2'][index]; + //} diff --git a/src/core/engine/picking/picking.js b/src/core/engine/picking/picking.js index 5a6cf44936..2dc17399e4 100755 --- a/src/core/engine/picking/picking.js +++ b/src/core/engine/picking/picking.js @@ -71,9 +71,9 @@ class Picking { }); } - _updateRender() { - this._renderer.render(this._pickingScene, this._camera, this._pickingTexture); - } + // _updateRender() { + // this._renderer.render(this._pickingScene, this._camera, this._pickingTexture); + // } _pick(point, normalisedPoint, layerId) { this._update(point); diff --git a/src/core/layer.js b/src/core/layer.js index bbc32c8df6..d3f3d489e4 100644 --- a/src/core/layer.js +++ b/src/core/layer.js @@ -168,6 +168,7 @@ export default class Layer extends Base { } else { scaleDefs[field] = cfg; } + console.log(options); return this; } shape(field, values) { @@ -373,11 +374,14 @@ export default class Layer extends Base { setActive(id, color) { this._activeIds = id; - this.layerMesh.material.setUniformsValue('u_activeId', id); if (!Array.isArray(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) { @@ -623,23 +627,31 @@ export default class Layer extends Base { } this._activeIds = featureId; // TODO 瓦片图层获取选中数据信息 - // const feature = this.layerSource.getSelectFeature(featureId); + const { feature, style } = this.getSelectFeature(featureId); const lnglat = this.scene.containerToLngLat(point2d); // const style = this.layerData[featureId - 1]; const target = { featureId, - // feature, - // style, + feature, + style, pixel: point2d, type, lnglat: { lng: lnglat.lng, lat: lnglat.lat } }; - if (featureId >= 0 || this._activeIds !== null) { // 拾取到元素,或者离开元素 + if (featureId >= 0 || this._activeIds >= 0) { // 拾取到元素,或者离开元素 this.emit(type, target); } }); } + getSelectFeature(featureId) { + const feature = this.layerSource.getSelectFeature(featureId); + const style = this.layerData[featureId - 1]; + return { + feature, + style + }; + } /** * 用于过滤数据 * @param {*} object 更新颜色和数据过滤 diff --git a/src/core/scene.js b/src/core/scene.js index 50823980fd..b83e20fb99 100644 --- a/src/core/scene.js +++ b/src/core/scene.js @@ -25,8 +25,8 @@ export default class Scene extends Base { _initEngine(mapContainer) { this._engine = new Engine(mapContainer, this); - this.registerMapEvent(); - // this._engine.run(); + // this.registerMapEvent(); + this._engine.run(); // this.workerPool = new WorkerPool(); compileBuiltinModules(); } diff --git a/src/core/source.js b/src/core/source.js index 96dcd12daf..8b8fd97a92 100644 --- a/src/core/source.js +++ b/src/core/source.js @@ -56,7 +56,9 @@ export default class Source extends Base { const { type = 'geojson' } = parser; const data = this.get('data'); 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); } /** diff --git a/src/geom/buffer/point/normalBuffer.js b/src/geom/buffer/point/normalBuffer.js index 755e7697a3..696cbb6290 100644 --- a/src/geom/buffer/point/normalBuffer.js +++ b/src/geom/buffer/point/normalBuffer.js @@ -13,7 +13,5 @@ export default function NormalBuffer(layerData) { attributes.sizes.push(size); }); - - return attributes; } diff --git a/src/geom/shader/point_frag.glsl b/src/geom/shader/point_frag.glsl index f14cbd46c2..30ff5a6623 100644 --- a/src/geom/shader/point_frag.glsl +++ b/src/geom/shader/point_frag.glsl @@ -20,11 +20,11 @@ const vec3 halo = vec3( 1.0 ); void main() { // 纹理坐标 #ifdef TEXCOORD_0 - vec2 pos = v_uv + gl_PointCoord / 512.0 * 64.0; - pos.y = 1.0 - pos.y; - vec4 textureColor = texture2D(u_texture, pos); - gl_FragColor =textureColor; - return; + vec2 pos = v_uv + gl_PointCoord / 512.0 * 64.0; + pos.y = 1.0 - pos.y; + vec4 textureColor = texture2D(u_texture, pos); + gl_FragColor =textureColor; + return; #endif if(v_color.a == 0.) discard; diff --git a/src/geom/shader/point_vert.glsl b/src/geom/shader/point_vert.glsl index d394e556fc..874376fe9a 100644 --- a/src/geom/shader/point_vert.glsl +++ b/src/geom/shader/point_vert.glsl @@ -16,7 +16,7 @@ void main() { mat4 matModelViewProjection = projectionMatrix * modelViewMatrix; v_color = a_color; v_color.a *= u_opacity; - if(pickingId == u_activeId) { + if(pickingId == u_activeId) { v_color = u_activeColor; } gl_Position = matModelViewProjection * vec4(position, 1.0); diff --git a/src/layer/render/point/drawFill.js b/src/layer/render/point/drawFill.js index 57671b491c..5bfad1cfcd 100644 --- a/src/layer/render/point/drawFill.js +++ b/src/layer/render/point/drawFill.js @@ -22,7 +22,8 @@ export default function DrawFill(layerData, layer) { geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.a_size, 3)); const material = new PolygonMaterial({ u_opacity: style.opacity, - u_activeColor: activeOption.fill + u_activeColor: activeOption.fill, + u_zoom: layer.scene.getZoom() }, { SHAPE: true }); diff --git a/src/layer/render/point/drawImage.js b/src/layer/render/point/drawImage.js index 801cb8e854..832db247b4 100644 --- a/src/layer/render/point/drawImage.js +++ b/src/layer/render/point/drawImage.js @@ -8,7 +8,7 @@ export default function DrawImage(layerData, layer) { const { strokeWidth, stroke, opacity } = style; const texture = layer.scene.image.texture; 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('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4)); diff --git a/src/layer/tile/imageTile.js b/src/layer/tile/imageTile.js index 563c65c933..b552c0d37c 100644 --- a/src/layer/tile/imageTile.js +++ b/src/layer/tile/imageTile.js @@ -2,6 +2,7 @@ import Tile from './tile'; import ImageBuffer from '../../geom/buffer/image'; import DrawImage from '../render/image/drawImage'; +import * as THREE from '../../core/three'; export default class ImageTile extends Tile { requestTileAsync() { // Making this asynchronous really speeds up the LOD framerate @@ -13,30 +14,34 @@ export default class ImageTile extends Tile { }, 0); } _requestTile() { - const urlParams = { - x: this._tile[0], - y: this._tile[1], - z: this._tile[2] - }; + const image = this._createDebugMesh(); + this._createMesh(image); + this.emit('tileLoaded'); + // return; + // const urlParams = { + // x: this._tile[0], + // y: this._tile[1], + // z: this._tile[2] + // }; - const url = this._getTileURL(urlParams); - const image = document.createElement('img'); + // const url = this._getTileURL(urlParams); + // const image = document.createElement('img'); - image.addEventListener('load', () => { - this._isLoaded = true; - this._createMesh(image); - this._ready = true; - }, false); + // image.addEventListener('load', () => { + // this._isLoaded = true; + // this._createMesh(image); + // this._ready = true; + // }, false); - // image.addEventListener('progress', event => {}, false); - // image.addEventListener('error', event => {}, false); + // // image.addEventListener('progress', event => {}, false); + // // image.addEventListener('error', event => {}, false); - image.crossOrigin = ''; + // image.crossOrigin = ''; - // Load image - image.src = url; + // // Load image + // image.src = url; - this._image = image; + // this._image = image; } _getBufferData(images) { const NW = this._tileBounds.getTopLeft(); @@ -61,6 +66,18 @@ export default class ImageTile extends Tile { this._object3D.add(mesh); 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() { if (!this._image) { return; @@ -68,7 +85,9 @@ export default class ImageTile extends Tile { this._image.src = ''; } - + getSelectFeature() { + return {}; + } destroy() { // Cancel any pending requests this._abortRequest(); diff --git a/src/layer/tile/tile.js b/src/layer/tile/tile.js index d3661c4525..dc504e13b7 100644 --- a/src/layer/tile/tile.js +++ b/src/layer/tile/tile.js @@ -37,10 +37,10 @@ export default class Tile extends Base { this._object3D.onBeforeRender = () => { }; this._isLoaded = false; - this._initControllers(); this.requestTileAsync(data => this._init(data)); } _init(data) { + this._initControllers(); this._creatSource(data); this._initTileAttrs(); this._mapping(); diff --git a/src/layer/tile/tileLayer.js b/src/layer/tile/tileLayer.js index 94b38b1fc0..624a237984 100644 --- a/src/layer/tile/tileLayer.js +++ b/src/layer/tile/tileLayer.js @@ -1,15 +1,22 @@ import Layer from '../../core/layer'; import source from '../../core/source'; import * as THREE from '../../core/three'; +import Global from '../../global'; +const { pointShape } = Global; +import { updateObjecteUniform } from '../../util/object3d-util'; import TileCache from './tileCache'; import pickingFragmentShader from '../../core/engine/picking/picking_frag.glsl'; 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'; export default class TileLayer extends Layer { constructor(scene, cfg) { - super(scene, cfg); - this._tileCache = new TileCache(50, this._destroyTile); + super(scene, { + ...cfg, + keepBuffer: 2 + }); + this._tileCache = new TileCache(100, this._destroyTile); this._crs = epsg3857; this._tiles = 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._tiles.frustumCulled = false; this._tileKeys = []; - this.tileList = []; + this.tileList = {}; } shape(field, values) { const layerType = this.get('layerType'); @@ -45,9 +52,9 @@ export default class TileLayer extends Layer { return new source(tileSourceCfg); } render() { - this._initControllers(); + // this._initControllers(); this._initMapEvent(); - this._initAttrs(); + // this._initAttrs(); this._initInteraction(); this.draw(); return this; @@ -56,86 +63,118 @@ export default class TileLayer extends Layer { this._object3D.add(this._tiles); this._calculateLOD(); } + drawTile() { } + zoomchange(ev) { super.zoomchange(ev); throttle(this._calculateLOD, 200); this._calculateLOD(); } + dragend(ev) { super.dragend(ev); this._calculateLOD(); } _calculateLOD() { - const viewPort = this.scene.getBounds().toBounds(); - const SE = viewPort.getSouthEast(); - const NW = viewPort.getNorthWest(); + /** + * 加载完成 active + * 需要显示 current + * 是否保留 retain + */ + this.updateTileList = []; const zoom = Math.round(this.scene.getZoom()) - 1; - const tileCount = Math.pow(2, zoom); 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 centerXY = centerPoint.divideBy(256).round(); - const minXY = NWPoint.divideBy(256).round(); - const maxXY = SEPoint.divideBy(256).round(); - // console.log(NW.lng, NW.lat, SE.lng, SE.lat, NWPonint, SEPonint); - let updateTileList = []; - this.tileList = []; - const halfx = 1; - const halfy = 1; - - if (!(centerPoint.x > NWPoint.x && centerPoint.x < SEPoint.x)) { // 地图循环的问题 - for (let i = 0; i < minXY.x; i++) { - for (let j = Math.min(0, minXY.y - halfy); j < Math.max(maxXY.y + halfy, tileCount); j++) { - this._updateTileList(updateTileList, i, j, zoom); - } - } - for (let i = maxXY.x; i < tileCount; i++) { - for (let j = Math.min(0, minXY.y - halfy); j < Math.max(maxXY.y + halfy, tileCount); j++) { - this._updateTileList(updateTileList, i, j, zoom); + const pixelBounds = this._getPixelBounds(); + const tileRange = this._pxBoundsToTileRange(pixelBounds); + const margin = this.get('keepBuffer'); + this.noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([ margin, -margin ]), + tileRange.getTopRight().add([ margin, -margin ])); + if (!(isFinite(tileRange.min.x) && + isFinite(tileRange.min.y) && + isFinite(tileRange.max.x) && + isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); } + for (let j = tileRange.min.y; j <= tileRange.max.y; j++) { + for (let i = tileRange.min.x; i <= tileRange.max.x; i++) { + const coords = [ i, j, zoom ]; + const tile = this.tileList[coords.join('_')]; + if (tile) { + tile.current = true; + } else { + 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++) { - for (let j = Math.max(0, minXY.y - halfy); j < Math.min(maxXY.y + halfy, tileCount); j++) { - this._updateTileList(updateTileList, i, j, zoom); - } - } - // 过滤掉已经存在的 - // tileList = tileList.filter(tile => { - // }) - updateTileList = updateTileList.sort((a, b) => { - const tile1 = a.split('_'); - const tile2 = b.split('_'); + 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; }); - updateTileList.forEach(key => { - this._requestTile(key, this); + this._pruneTiles(); + // 更新瓦片数据 + this.updateTileList.forEach(coords => { + const key = coords.join('_'); + if (this.tileList[key].current) { + this._requestTile(key, this); + } }); - this._removeOutTiles(); } - - _updateTileList(updateTileList, x, y, z) { + _getShape(layerData) { + 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('_'); - this.tileList.push(key); - if (this._tileKeys.indexOf(key) === -1 && updateTileList.indexOf(key) === -1) { - updateTileList.push(key); + const tile = this.tileList[key]; + if (tile) { + tile.current = true; + } else { + this.tileList[key] = { + current: true, + active: false, + coords: key.split('_') + }; + this.updateTileList.push(key); } } _requestTile(key, layer) { + const t = this.tileList[key]; + if (!t) { + return; + } let tile = this._tileCache.getTile(key); if (!tile) { tile = this._createTile(key, layer); tile.on('tileLoaded', () => { - if (this.tileList.indexOf(key) === -1) { - return; - } + t.active = true; const mesh = tile.getMesh(); mesh.name = key; this._tileCache.setTile(tile, key); @@ -144,12 +183,16 @@ export default class TileLayer extends Layer { this._tiles.add(tile.getMesh()); this._addPickTile(tile.getMesh()); } + this.scene._engine.update(); + this._pruneTiles(); }); } else { this._tiles.add(tile.getMesh()); + t.active = true; this._addPickTile(tile.getMesh()); this._tileKeys.push(key); this.scene._engine.update(); + this._pruneTiles(); } } _addPickTile(meshobj) { @@ -165,19 +208,104 @@ export default class TileLayer extends Layer { this._pickTiles.add(pickingMesh); } - // 移除视野外的tile - _removeOutTiles() { - for (let i = this._tiles.children.length - 1; i >= 0; i--) { - const tile = this._tiles.children[i]; - const key = tile.name; - if (this.tileList.indexOf(key) === -1) { - const tileObj = this._tileCache.getTile(key); - tileObj && tileObj._abortRequest(); - this._tiles.remove(tile); + getSelectFeature(id) { + let feat = null; + this._tileKeys.forEach(key => { + const tile = this._tileCache.getTile(key); + const feature = tile ? tile.getSelectFeature(id) : null; + if (feature !== null) { + feat = feature; + return; + } + }); + 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() { if (!this._tiles || !this._tiles.children) { return; @@ -187,10 +315,51 @@ export default class TileLayer extends Layer { 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) { tile.destroy(); tile = null; } - desttroy() { + destroy() { } } diff --git a/src/layer/tile/vectorTile.js b/src/layer/tile/vectorTile.js index e4a1081f2b..e7eb496435 100644 --- a/src/layer/tile/vectorTile.js +++ b/src/layer/tile/vectorTile.js @@ -1,6 +1,6 @@ import Tile from './tile'; 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 MaskMaterial from '../../geom/material/tile/maskMaterial'; import { getRender } from '../render/index'; @@ -39,6 +39,9 @@ export default class VectorTile extends Tile { }); } _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.onBeforeRender = renderer => { this._renderMask(renderer); @@ -52,6 +55,14 @@ export default class VectorTile extends Tile { 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 maskScene = new THREE.Scene(); this.maskScene = maskScene; const tileMesh = this._tileMaskMesh(); @@ -97,6 +108,13 @@ export default class VectorTile extends Tile { this.xhrRequest.abort(); } + getSelectFeature(id) { + const featureIndex = this.source.originData.featureKeys[id]; + if (featureIndex) { + return this.source.originData.dataArray[featureIndex]; + } + return null; + } destroy() { super.destroy(); destoryObject(this.maskScene); diff --git a/src/source/parser/geojson.js b/src/source/parser/geojson.js index 6bbaf533f4..7636ec95a9 100644 --- a/src/source/parser/geojson.js +++ b/src/source/parser/geojson.js @@ -1,27 +1,35 @@ import * as turfMeta from '@turf/meta'; import { getCoords } from '@turf/invariant'; - +import { BKDRHash } from '../../util/bkdr-hash'; export default function geoJSON(data, cfg) { const resultData = []; + const featureKeys = {}; data.features = data.features.filter(item => { return item != null && item.geometry && item.geometry.type && item.geometry.coordinates && item.geometry.coordinates.length > 0; }); - // 数据为空时处理 turfMeta.flattenEach(data, (currentFeature, featureIndex) => { // 多个polygon 拆成一个 const coord = getCoords(currentFeature); let id = featureIndex + 1; - if (cfg.idField) { - id = currentFeature.properties[cfg.idField]; - } + // if (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 = { ...currentFeature.properties, coordinates: coord, - _id: id + _id: currentFeature.properties[cfg.idField] }; resultData.push(dataItem); }); return { - dataArray: resultData + dataArray: resultData, + featureKeys }; } diff --git a/src/util/bkdr-hash.js b/src/util/bkdr-hash.js new file mode 100644 index 0000000000..12848786a1 --- /dev/null +++ b/src/util/bkdr-hash.js @@ -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; +}