From 4ce9d602569850e26ba550f0cf8b7753abf054c6 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Fri, 14 Jun 2019 10:08:42 +0800 Subject: [PATCH] feat(source): reuse tileSource --- demos/vectorTile.html | 2 +- demos/vectorTilepolygon.html | 36 +++++------ src/core/controller/buffer.js | 16 +++-- src/core/controller/mapping.js | 2 +- src/core/engine/picking/picking.js | 18 +++++- src/core/layer.js | 21 ++++--- src/core/scene.js | 4 +- src/core/source.js | 2 +- src/geom/material/lineMaterial.js | 8 +-- src/geom/shader/line_vert.glsl | 3 +- src/index.js | 2 + src/layer/render/polygon/drawFill.js | 4 ++ src/layer/tile/tile.js | 16 ++++- src/layer/tile/tileCache.js | 6 ++ src/layer/tile/tileLayer.js | 74 ++++++++++------------ src/layer/tile/vectorTile.js | 32 +++++----- src/source/parser/geojson.js | 15 +++-- src/source/parser/mvt.js | 23 +++---- src/source/tileDataCache.js | 20 ++++++ src/source/tileSource.js | 92 ++++++++++++++++++++++++++++ src/util/bkdr-hash.js | 13 ++++ 21 files changed, 286 insertions(+), 123 deletions(-) create mode 100644 src/source/tileDataCache.js create mode 100644 src/source/tileSource.js diff --git a/demos/vectorTile.html b/demos/vectorTile.html index db74c5daf0..06b7983dbb 100644 --- a/demos/vectorTile.html +++ b/demos/vectorTile.html @@ -43,7 +43,7 @@ scene.on('loaded', () => { // 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://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/china/all_point/{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:'layer', diff --git a/demos/vectorTilepolygon.html b/demos/vectorTilepolygon.html index 799ec23705..9780ad8624 100644 --- a/demos/vectorTilepolygon.html +++ b/demos/vectorTilepolygon.html @@ -35,47 +35,41 @@ const scene = new L7.Scene({ }); window.scene = scene; var colorHash = new ColorHash(); + scene.on('loaded', () => { - const layer = scene.VectorTileLayer({ - zIndex:0, - layerType:'polygon' - }) - //.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://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/china/village/{z}/{x}/{y}.pbf',{ + + const provinceSource = new L7.TileSource('http://alipay-rmsdeploy-image.cn-hangzhou.alipay.aliyun-inc.com/thinkgis/tile/china/province/{z}/{x}/{y}.pbf',{ parser:{ type: 'mvt', sourceLayer:'layer', idField:'code', - maxZoom: 17, + maxZoom: 5, } +}) +console.log(provinceSource); + const layer = scene.VectorTileLayer({ + zIndex:0, + layerType:'line' }) + .source(provinceSource) .scale({ total:{ type:'linear', min:0, - max:5000 + max:5000000 } }) - .shape('fill') + .shape('line') .size(2) .active(false) .color('total', ['#ffffe5','#fff7bc','#fee391','#fec44f','#fe9929','#ec7014','#cc4c02','#993404','#662506']) .style({ opacity:1.0 }) - .render(); - layer.on('mousemove',(feature)=>{ - console.log(feature); - }) - console.log(layer); + .render(); + }); -//OBJECTID',(id)=>{ - // const index = id % 8; - //return ['#9e0142','#d53e4f','#f46d43','#fdae61','#fee08b','#ffffbf','#e6f598','#abdda4','#66c2a5','#3288bd','#5e4fa2'][index]; - //} + diff --git a/src/core/controller/buffer.js b/src/core/controller/buffer.js index 1f6b7081c7..518c1adfa8 100644 --- a/src/core/controller/buffer.js +++ b/src/core/controller/buffer.js @@ -18,10 +18,18 @@ export default class BufferController { const colorAttr = this.mesh.mesh.geometry.attributes.a_color; const pickAttr = this.mesh.mesh.geometry.attributes.pickingId; pickAttr.array.forEach((id, index) => { - id = Math.abs(id); - const color = colorKey[id]; - id = Math.abs(id); - const item = filterData[id - 1]; + let newId = Math.abs(id); + let item = null; + let color = null; + if (this.mesh.layerSource.data.featureKeys) { // hash数据映射 + newId = this.mesh.layerSource.data.featureKeys[newId].index; + item = filterData[newId]; + color = colorKey[item.id]; + } else { + item = filterData[newId - 1]; + color = colorKey[newId]; + } + if (item.hasOwnProperty('filter') && item.filter === false) { colorAttr.array[index * 4 + 0] = 0; colorAttr.array[index * 4 + 1] = 0; diff --git a/src/core/controller/mapping.js b/src/core/controller/mapping.js index d5054d1053..5020431735 100644 --- a/src/core/controller/mapping.js +++ b/src/core/controller/mapping.js @@ -34,8 +34,8 @@ export default class Mapping { this.mesh.set('scaleController', scaleController); } _createScale(field) { - // TODO scale更新 const scales = this.mesh.get('scales'); + this._initControllers(); // scale更新 let scale = scales[field]; if (!scale) { scale = this.createScale(field); diff --git a/src/core/engine/picking/picking.js b/src/core/engine/picking/picking.js index a332d1ea51..cef3c5057d 100755 --- a/src/core/engine/picking/picking.js +++ b/src/core/engine/picking/picking.js @@ -50,8 +50,7 @@ class Picking { } _update(point) { const texture = this._pickingTexture; - // this._pickingTexture - this._renderer.render(this._pickingScene, this._camera, this._pickingTexture); + this._renderer.render(this._pickingScene, this._camera, texture); this.pixelBuffer = new Uint8Array(4); this._renderer.readRenderTargetPixels(texture, point.x, this._height - point.y, 1, 1, this.pixelBuffer); @@ -62,8 +61,23 @@ class Picking { index === id ? object.visible = true : object.visible = false; }); } + _layerIsVisable(object) { + const layers = this._world.getLayers(); + let isVisable = false; + for (let i = 0; i < layers.length; i++) { + const layer = layers[i]; + if (object.name === layer.layerId) { + isVisable = layer.get('visible'); + break; + } + } + return isVisable; + } _pickAllObject(point, normalisedPoint) { this.world.children.forEach((object, index) => { + if (!this._layerIsVisable(object)) { + return; + } this._filterObject(index); const item = this._pick(point, normalisedPoint, object.name); item.type = point.type; diff --git a/src/core/layer.js b/src/core/layer.js index 26fb89dc19..cc7c82f962 100644 --- a/src/core/layer.js +++ b/src/core/layer.js @@ -460,13 +460,15 @@ export default class Layer extends Base { _initEvents() { this.scene.on('pick-' + this.layerId, e => { let { featureId, point2d, type } = e; - if (featureId < 0 && this._activeIds !== null) { - type = 'mouseleave'; - } - this._activeIds = featureId; // TODO 瓦片图层获取选中数据信息 const lnglat = this.scene.containerToLngLat(point2d); - const { feature, style } = this.getSelectFeature(featureId, lnglat); + let feature = null; + let style = null; + if (featureId !== -999) { + const res = this.getSelectFeature(featureId, lnglat); + feature = res.feature; + style = res.style; + } const target = { featureId, feature, @@ -475,9 +477,14 @@ export default class Layer extends Base { type, lnglat: { lng: lnglat.lng, lat: lnglat.lat } }; - if (featureId >= 0 || this._activeIds >= 0) { // 拾取到元素,或者离开元素 + if (featureId >= 0) { // 拾取到元素,或者离开元素 this.emit(type, target); } + if (featureId < 0 && this._activeIds >= 0) { + type = 'mouseleave'; + this.emit(type, target); + } + this._activeIds = featureId; }); } @@ -538,7 +545,7 @@ export default class Layer extends Base { offset = 1; } this._object3D.position && (this._object3D.position.z = offset * Math.pow(2, 20 - zoom)); - if (zoom < minZoom || zoom > maxZoom) { + if (zoom < minZoom || zoom >= maxZoom) { this._object3D.visible = false; } else if (this.get('visible')) { this._object3D.visible = true; diff --git a/src/core/scene.js b/src/core/scene.js index 50823980fd..6890247663 100644 --- a/src/core/scene.js +++ b/src/core/scene.js @@ -105,7 +105,9 @@ export default class Scene extends Base { this._container.addEventListener(event, e => { // 要素拾取 e.pixel || (e.pixel = e.point); - this._engine._picking.pickdata(e); + requestAnimationFrame(() => { + this._engine._picking.pickdata(e); + }); }, false); }); } diff --git a/src/core/source.js b/src/core/source.js index a529d460e9..cd22c837f5 100644 --- a/src/core/source.js +++ b/src/core/source.js @@ -58,7 +58,7 @@ export default class Source extends Base { this.originData = getParser(type)(data, parser); // this.data = { // dataArray: clone(this.originData.dataArray) - // }; + // }; // TODO 关闭数据备份 this.data = this.originData; if (this.data !== null) { this.data.extent = extent(this.data.dataArray); diff --git a/src/geom/material/lineMaterial.js b/src/geom/material/lineMaterial.js index 858a4b93e7..68754d0762 100644 --- a/src/geom/material/lineMaterial.js +++ b/src/geom/material/lineMaterial.js @@ -17,8 +17,8 @@ export function LineMaterial(options) { }, vertexShader: vs, fragmentShader: fs, - transparent: true, - blending: THREE.AdditiveBlending + transparent: true + // blending: THREE.AdditiveBlending }); return material; } @@ -50,8 +50,8 @@ export function MeshLineMaterial(options, defines) { defines, vertexShader: vs, fragmentShader: fs, - transparent: true, - blending: THREE.AdditiveBlending + transparent: true + // blending: THREE.AdditiveBlending }); return material; } diff --git a/src/geom/shader/line_vert.glsl b/src/geom/shader/line_vert.glsl index 4916b86390..74a5d3c134 100644 --- a/src/geom/shader/line_vert.glsl +++ b/src/geom/shader/line_vert.glsl @@ -18,7 +18,6 @@ void main() { vTime = 1.0- (mod(u_time*50.,3600.)- position.z) / 100.; // vTime = 1.0- (28800. + mod(u_time* 10.,28800.)- position.z / 1000.) / 100.; #endif - gl_Position = matModelViewProjection * vec4(position.xy, 0., 1.0); - gl_Position.z += 2. * gl_Position.w; + gl_Position = matModelViewProjection * vec4(position.xy, 10., 1.0); worldId = id_toPickColor(pickingId); } \ No newline at end of file diff --git a/src/index.js b/src/index.js index 399f8df9ec..368540c528 100755 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ import Scene from './core/scene'; import Global from './global'; import Source from './core/source'; +import TileSource from './source/tileSource'; import { registerParser, registerTransform } from './source'; import { registerInteraction, getInteraction } from './interaction'; import { registerLayer } from './layer'; @@ -11,6 +12,7 @@ export { version, Scene, Source, + TileSource, registerParser, registerTransform, registerLayer, diff --git a/src/layer/render/polygon/drawFill.js b/src/layer/render/polygon/drawFill.js index 11ae385a2e..111561ae49 100644 --- a/src/layer/render/polygon/drawFill.js +++ b/src/layer/render/polygon/drawFill.js @@ -26,6 +26,10 @@ export default function DrawPolygonFill(layerData, layer) { SHAPE: false }); const fillPolygonMesh = new THREE.Mesh(geometry, material); + delete attributes.vertices; + delete attributes.colors; + delete attributes.pickingIds; + delete attributes.normals; return fillPolygonMesh; } diff --git a/src/layer/tile/tile.js b/src/layer/tile/tile.js index 183725977c..7c862018f2 100644 --- a/src/layer/tile/tile.js +++ b/src/layer/tile/tile.js @@ -30,10 +30,14 @@ export default class Tile extends Base { this.requestTileAsync(data => this._init(data)); } _init(data) { - this._creatSource(data); + // this._creatSource(data); // 获取Source + this.layerSource = data; + if (this.layerSource.data === null) { + this.isValid = false; return; } + this.isValid = true; this._initControllers(); this._createMesh(); } @@ -41,6 +45,16 @@ export default class Tile extends Base { this._initControllers(); this._createMesh(); } + requestTileAsync(done) { + const data = this.layer.tileSource.getTileData(this._tile[0], this._tile[1], this._tile[2]); + if (data.loaded) { + done(data.data); + } else { + data.data.then(data => { + done(data); + }); + } + } _initControllers() { const mappingCtr = new Controller.Mapping({ layer: this.layer, diff --git a/src/layer/tile/tileCache.js b/src/layer/tile/tileCache.js index 9681b739f5..9f238a370f 100644 --- a/src/layer/tile/tileCache.js +++ b/src/layer/tile/tileCache.js @@ -14,4 +14,10 @@ export default class TileCache { destory() { this._cache.clear(); } + setNeedUpdate() { + this._cache._order.forEach(key => { + const tile = this._cache.get(key); + tile.needUpdate = true; + }); + } } diff --git a/src/layer/tile/tileLayer.js b/src/layer/tile/tileLayer.js index 1d64174acb..9b46575721 100644 --- a/src/layer/tile/tileLayer.js +++ b/src/layer/tile/tileLayer.js @@ -1,13 +1,12 @@ import Layer from '../../core/layer'; import Util from '../../util'; import diff from '../../util/diff'; -import source from '../../core/source'; +import TileSource from '../../source/tileSource'; import * as THREE from '../../core/three'; import Controller from '../../core/controller/index'; import Global from '../../global'; const { pointShape } = Global; import TileCache from './tileCache'; -import { deepMix } from '@antv/util'; 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'; @@ -35,24 +34,15 @@ export default class TileLayer extends Layer { this.shape = field; return this; } - source(url, cfg = {}) { - this.url = url; - this.sourceCfg = cfg; - this.sourceCfg.mapType = this.scene.mapType; - this.set('minSourceZoom', this.sourceCfg.parser && this.sourceCfg.parser.minZoom || 0); - this.set('maxSourceZoom', this.sourceCfg.parser && this.sourceCfg.parser.maxZoom || 18); - return this; - } - tileSource(data, cfg) { - if (data instanceof source) { - return data; + source(data, cfg = {}) { + if (data instanceof TileSource) { + data.set('mapType', this.scene.mapType); + this.tileSource = data; + } else { + cfg.mapType = this.scene.mapType; + this.tileSource = new TileSource(data, cfg); } - const tileSourceCfg = { - data, - zoom: this.scene.getZoom() - }; - deepMix(tileSourceCfg, this.sourceCfg, cfg); - return new source(tileSourceCfg); + return this; } _initControllers() { const pickCtr = new Controller.Picking({ layer: this }); @@ -61,10 +51,6 @@ export default class TileLayer extends Layer { this.set('interacionController', interactionCtr); } render() { - - if (this.type !== 'image') { - this._initControllers(); - } this._visibleWithZoom(); this._updateDraw(); this.scene._engine.update(); @@ -84,7 +70,6 @@ export default class TileLayer extends Layer { requestAnimationFrame(() => { this._calculateLOD(); }); - // throttle(this._calculateLOD, 200); this._calculateLOD(); } @@ -93,9 +78,9 @@ export default class TileLayer extends Layer { requestAnimationFrame(() => { this._calculateLOD(); }); - // this._calculateLOD(); } + _calculateLOD() { /** * 加载完成 active @@ -109,16 +94,18 @@ export default class TileLayer extends Layer { const maxSourceZoom = this.get('maxSourceZoom'); const currentZoom = this.scene.getZoom(); this.tileZoom = zoom > maxSourceZoom ? maxSourceZoom : zoom; - if (currentZoom < minZoom || currentZoom > maxZoom || currentZoom < minSourceZoom) { + if (currentZoom < minZoom || currentZoom >= maxZoom || currentZoom < minSourceZoom) { this._removeTiles(); - this.hide(); + this._object3D.visible = false; return; + } else if (this.get('visible')) { + this._object3D.visible = true; } this.show(); this.updateTileList = []; const center = this.scene.getCenter(); const centerPoint = this._crs.lngLatToPoint(toLngLat(center.lng, center.lat), this.tileZoom); - const centerXY = centerPoint.divideBy(256).round(); + const centerXY = centerPoint.divideBy(256).floor(); const pixelBounds = this._getPixelBounds(); const tileRange = this._pxBoundsToTileRange(pixelBounds); const margin = this.get('keepBuffer'); @@ -228,7 +215,10 @@ export default class TileLayer extends Layer { this._pruneTiles(); return; } - tile.updateColor(); + if (tile.needUpdate) { + tile.updateColor(); + tile.needUpdate = false; + } this._tiles.add(tile.getMesh()); t.active = true; this._addPickTile(tile.getMesh()); @@ -249,8 +239,9 @@ export default class TileLayer extends Layer { // 根据距离优先级查找 getSelectFeature(id, lnglat) { const zoom = this.tileZoom; + const tilePoint = this._crs.lngLatToPoint(toLngLat(lnglat.lng, lnglat.lat), zoom); - const tileXY = tilePoint.divideBy(256).round(); + const tileXY = tilePoint.divideBy(256).floor(); const key = [ tileXY.x, tileXY.y, zoom ].join('_'); const tile = this._tileCache.getTile(key); const feature = tile ? tile.getSelectFeature(id) : null; @@ -351,6 +342,7 @@ export default class TileLayer extends Layer { _removeTiles() { + this.hide(); if (!this._tiles || !this._tiles.children) { return; } @@ -415,32 +407,28 @@ export default class TileLayer extends Layer { const preStyle = this.get('preStyleOption'); const nextStyle = this.get('styleOptions'); if (preAttrs === undefined && preStyle === undefined) { // 首次渲染 - // this._mapping(); this._setPreOption(); this._scaleByZoom(); - // this._initInteraction(); + this._initControllers(); + this._initInteraction(); this._initMapEvent(); this.draw(); this._setPreOption(); return; } - if (!this._tiles.children.length > 0) { - this._setPreOption(); + if (!this._tiles.children.length > 0 || !this._object3D.visible) { return; } - if (!Util.isEqual(preAttrs.color, nextAttrs.color)) { + // 更新数据颜色 过滤 filter + if (!Util.isEqual(preAttrs.color, nextAttrs.color) || !Util.isEqual(preAttrs.filter, nextAttrs.filter)) { + this._tileCache.setNeedUpdate(); this._tiles.children.forEach(tile => { - this._tileCache.getTile(tile.name).updateColor(); + const tileObj = this._tileCache.getTile(tile.name); + tileObj.updateColor(); + tileObj.needUpdate = false; this.scene._engine.update(); }); } - // 更新数据过滤 filter - if (!Util.isEqual(preAttrs.filter, nextAttrs.filter)) { - // 更新color; - this._tiles.children(tile => { - this._tileCache.get(tile.name).updateColor(); - }); - } // 更新Size if (!Util.isEqual(preAttrs.size, nextAttrs.size)) { // this._tiles.children(tile => { diff --git a/src/layer/tile/vectorTile.js b/src/layer/tile/vectorTile.js index 571dc32a02..6508081caf 100644 --- a/src/layer/tile/vectorTile.js +++ b/src/layer/tile/vectorTile.js @@ -5,15 +5,15 @@ import * as THREE from '../../core/three'; import MaskMaterial from '../../geom/material/tile/maskMaterial'; import { getRender } from '../render/index'; export default class VectorTile extends Tile { - requestTileAsync(done) { - // Making this asynchronous really speeds up the LOD framerate - setTimeout(() => { - if (!this._mesh) { - // this._mesh = this._createMesh(); - this._requestTile(done); - } - }, 0); - } + // requestTileAsync(done) { + // // Making this asynchronous really speeds up the LOD framerate + // setTimeout(() => { + // if (!this._mesh) { + // // this._mesh = this._createMesh(); + // this._requestTile(done); + // } + // }, 0); + // } _requestTile(done) { const urlParams = { x: this._tile[0], @@ -58,8 +58,9 @@ export default class VectorTile extends Tile { } else { this._object3D = this.mesh; } - - this.emit('tileLoaded'); + setTimeout(() => { + this.emit('tileLoaded'); + }, 0); return this._object3D; } _renderMask(renderer) { @@ -88,7 +89,7 @@ export default class VectorTile extends Tile { // config the stencil buffer to collect data for testing this.layer.scene._engine.renderScene(maskScene); context.colorMask(true, true, true, true); - context.depthMask(true); + context.depthMask(false); renderer.clearDepth(); // only render where stencil is set to 1 @@ -117,8 +118,9 @@ export default class VectorTile extends Tile { this.xhrRequest.abort(); } getSelectFeature(id) { - const featureIndex = this.layerSource.originData.featureKeys[id]; - if (featureIndex) { + const featurekey = this.layerSource.originData.featureKeys[id]; + if (featurekey && featurekey.index !== undefined) { + const featureIndex = featurekey.index; return this.layerSource.originData.dataArray[featureIndex]; } return null; @@ -129,7 +131,5 @@ export default class VectorTile extends Tile { this._object3D = null; this.maskScene = null; this.layerData = null; - this.layerSource.destroy(); - this.layerSource = null; } } diff --git a/src/source/parser/geojson.js b/src/source/parser/geojson.js index 79e49bd68d..67b3c89db6 100644 --- a/src/source/parser/geojson.js +++ b/src/source/parser/geojson.js @@ -1,6 +1,6 @@ import * as turfMeta from '@turf/meta'; import { getCoords } from '@turf/invariant'; -import { BKDRHash } from '../../util/bkdr-hash'; +import { djb2hash } from '../../util/bkdr-hash'; export default function geoJSON(data, cfg) { const resultData = []; const featureKeys = {}; @@ -8,19 +8,18 @@ export default function geoJSON(data, cfg) { return item != null && item.geometry && item.geometry.type && item.geometry.coordinates && item.geometry.coordinates.length > 0; }); // 数据为空时处理 + let i = 0; turfMeta.flattenEach(data, (currentFeature, featureIndex) => { // 多个polygon 拆成一个 const coord = getCoords(currentFeature); let id = featureIndex + 1; if (cfg.idField && 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); - // } + id = djb2hash(value) % 1000019; + featureKeys[id] = { + index: i++, + idField: value + }; } - featureKeys[id] = featureIndex; const dataItem = { ...currentFeature.properties, coordinates: coord, diff --git a/src/source/parser/mvt.js b/src/source/parser/mvt.js index 1d77d9592e..f549b818c5 100644 --- a/src/source/parser/mvt.js +++ b/src/source/parser/mvt.js @@ -16,19 +16,20 @@ export default function mvt(data, cfg) { if (geofeature.geometry.type === 'Polygon' && geofeature.geometry.coordinates[0].length < 20) { continue; } - const newfc = { - geometry: geofeature.geometry, - type: 'Feature', - properties: { - total: geofeature.properties.total, - province: geofeature.properties.province, - bc_grade: geofeature.properties.bc_grade - } - - }; - features.push(newfc); + // const newfc = { + // geometry: geofeature.geometry, + // type: 'Feature', + // properties: { + // total: geofeature.properties.total, + // province: geofeature.properties.province, + // bc_grade: geofeature.properties.bc_grade, + // code: geofeature.properties.code || geofeature.properties.adcode + // } + // }; + features.push(geofeature); } + // console.log(features); const geodata = { type: 'FeatureCollection', features diff --git a/src/source/tileDataCache.js b/src/source/tileDataCache.js new file mode 100644 index 0000000000..cba4f7cc02 --- /dev/null +++ b/src/source/tileDataCache.js @@ -0,0 +1,20 @@ +import LRUCache from '../util/lru-cache'; +export default class TileDataCache { + constructor(limit = 50, tileDestroy) { + this._cache = new LRUCache(limit, tileDestroy); + } + + getTile(key) { + return this._cache.get(key); + } + + setTile(tile, key) { + this._cache.set(key, tile); + } + removeTile(key) { + return this._cache.delete(key); + } + destory() { + this._cache.clear(); + } +} diff --git a/src/source/tileSource.js b/src/source/tileSource.js new file mode 100644 index 0000000000..1c85a7c26b --- /dev/null +++ b/src/source/tileSource.js @@ -0,0 +1,92 @@ +import Source from '../core/source'; +import { getArrayBuffer } from '../util/ajax'; +import TileDataCache from './tileDataCache'; +const tileURLRegex = /\{([zxy])\}/g; +export default class TileSource extends Source { + constructor(url, cfg) { + super(cfg); + this.cfg = cfg; + this.urlTemplate = url; + this._tileDataCache = new TileDataCache(50, this.tileDestroy); + this.type = 'tile'; + + + } + getTileData(x, y, z) { + const key = [ x, y, z ].join('_'); + let tileData = this._tileDataCache.getTile(key); + if (!tileData) { + const tiledataPromise = new Promise(resolve => { + if (tileData) { + setTimeout(() => { + resolve(tileData); + }, 0); + } else { + this._requestTileData(x, y, z, resolve); + } + }); + tileData = { + loading: true, + data: tiledataPromise + }; + this._tileDataCache.setTile(tileData, key); + return tileData; + } + return tileData; + + } + _init() { + const parser = this.get('parser'); + this.set('minSourceZoom', parser && parser.minZoom || 0); + this.set('maxSourceZoom', parser && parser.maxZoom || 18); + } + _generateSource(x, y, z, data) { + this.cfg.parser.tile = [ x, y, z ]; + const tileData = new Source({ + ...this.cfg, + mapType: this.get('mapType'), + data, + tile: [ x, y, z ] + }); + return tileData; + } + _requestTileData(x, y, z, done) { + const urlParams = { x, y, z }; + const url = this._getTileURL(urlParams); + const key = [ x, y, z ].join('_'); + this.xhrRequest = getArrayBuffer({ url }, (err, data) => { + if (err) { + this._noData = true; + this._tileDataCache.setTile({ loaded: true, data: { data: null } }, key); + return; + } + const tileData = this._generateSource(x, y, z, data.data); + this._tileDataCache.setTile({ loaded: true, data: tileData }, key); + done(tileData); + }); + + } + _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]; + }); + } + tileDestroy(tile) { + if (!tile || !tile.data || tile.loading) { + return; + } + const tileData = tile.data; + tileData.destroy(); + tileData.data.dataArray.length = 0; + tileData.data.featureKeys = null; + tileData.originData.dataArray.length = 0; + tileData.originData.featureKeys = null; + } + +} diff --git a/src/util/bkdr-hash.js b/src/util/bkdr-hash.js index 12848786a1..c916b85a48 100644 --- a/src/util/bkdr-hash.js +++ b/src/util/bkdr-hash.js @@ -14,3 +14,16 @@ export function BKDRHash(str) { } return hash; } +export function djb2hash(str) { + let hash = 5381, + i = str.length; + + while (i) { + hash = (hash * 33) ^ str.charCodeAt(--i); + } + + /* JavaScript does bitwise operations (like XOR, above) on 32-bit signed + * integers. Since we want the results to be always positive, convert the + * signed int to an unsigned by doing an unsigned bitshift. */ + return hash >>> 0; +}