From 48de69f7d9d49ccd5b10f9652fc3a949d705bee0 Mon Sep 17 00:00:00 2001 From: thinkinggis Date: Fri, 29 Mar 2019 10:21:10 +0800 Subject: [PATCH] feat(layer): add repaint method --- demos/01_animatePoint.html | 105 ++++++++++ demos/01_point_circle.html | 45 ++-- demos/02_animateline.html | 100 +++++++++ src/core/atlas/font-manager.js | 227 +++++++++++++++++++++ src/core/atlas/icon-manager.js | 0 src/core/atlas/lru-cache.js | 71 +++++++ src/core/layer.js | 179 +++++++++------- src/core/scene.js | 8 - src/core/source.js | 46 ++++- src/geom/base.js | 21 ++ src/geom/buffer/point/text.js | 3 +- src/geom/geom.js | 62 ------ src/geom/shader/point_meshLine_vert.glsl | 2 +- src/geom/shader/polygon_vert.glsl | 2 +- src/geom/shader/text_frag.glsl | 2 + src/geom/shader/text_vert.glsl | 2 + src/geom/shape/path.js | 2 +- src/index.js | 13 +- src/interaction/active.js | 16 ++ src/interaction/base.js | 87 ++++++++ src/interaction/factory.js | 14 ++ src/interaction/index.js | 9 + src/interaction/select.js | 12 ++ src/layer/heatmapLayer.js | 10 +- src/layer/index.js | 1 + src/layer/lineLayer.js | 140 +++++++++---- src/layer/pointLayer.js | 34 ++- src/layer/polygonLayer.js | 5 +- src/layer/render/line/drawLine.js | 16 ++ src/layer/render/point/drawText.js | 2 +- src/map/AMap.js | 3 + src/source/transform/cluster.js | 5 +- test/unit/source/transfrom/cluster-spec.js | 6 +- 33 files changed, 1017 insertions(+), 233 deletions(-) create mode 100644 demos/01_animatePoint.html create mode 100644 demos/02_animateline.html create mode 100644 src/core/atlas/font-manager.js create mode 100644 src/core/atlas/icon-manager.js create mode 100644 src/core/atlas/lru-cache.js create mode 100644 src/geom/base.js delete mode 100644 src/geom/geom.js create mode 100644 src/interaction/active.js create mode 100644 src/interaction/base.js create mode 100644 src/interaction/factory.js create mode 100644 src/interaction/index.js create mode 100644 src/interaction/select.js diff --git a/demos/01_animatePoint.html b/demos/01_animatePoint.html new file mode 100644 index 0000000000..96f2acf9c4 --- /dev/null +++ b/demos/01_animatePoint.html @@ -0,0 +1,105 @@ + + + + + + + + + + point_circle + + + + +
+ + + + + + + + diff --git a/demos/01_point_circle.html b/demos/01_point_circle.html index fef2948b0d..ce3ac03fb3 100644 --- a/demos/01_point_circle.html +++ b/demos/01_point_circle.html @@ -39,39 +39,46 @@ const scene = new L7.Scene({ center: [ 120.19382669582967, 30.258134 ], pitch: 0, zoom: 12, - maxZoom:14, - minZoom:11, + maxZoom:20, + minZoom:0, }); window.scene = scene; scene.on('loaded', () => { $.get('https://gw.alipayobjects.com/os/rmsportal/epnZEheZeDgsiSjSPcCv.json', data => { const circleLayer = scene.PointLayer({ - zIndex: 2, + zIndex: 0, }) - .source(data) - .shape('circle') - .size('value', [ 2, 30]) // default 1 - .scale('value', { - type:'log', + .source(data,{ + isCluster:true }) + .shape('hexagon') + .size('point_count', [ 2, 30]) // default 1 //.size('value', [ 10, 300]) // default 1 .active(true) - .filter('value', field_8 => { - return field_8 * 1 > 500; - }) - .color('type', colorObj.blue) + .color('#2894E0') .style({ stroke: 'rgb(255,255,255)', strokeWidth: 1, - opacity: 1. + opacity: 0.8 }) .render(); - console.log(circleLayer); - var a = circleLayer.getLegendCfg('type','color'); - console.log(a); - circleLayer.on('click',(e)=>{ - console.log(e); - }) + window.circleLayer = circleLayer; + const layerText = scene.PointLayer({ + zIndex: 3 + }) + .source(circleLayer.layerSource) + .shape('point_count', 'text') + .active(true) + .size('point_count', [ 0, 16]) // default 1 + .color('#f00') + .style({ + stroke: '#999', + strokeWidth: 0, + opacity: 1.0 + }) + .render(); + console.log(layerText); + }); }); diff --git a/demos/02_animateline.html b/demos/02_animateline.html new file mode 100644 index 0000000000..9d78576db4 --- /dev/null +++ b/demos/02_animateline.html @@ -0,0 +1,100 @@ + + + + + + + + + + animateLine + + + + +
+ + + + + + + + diff --git a/src/core/atlas/font-manager.js b/src/core/atlas/font-manager.js new file mode 100644 index 0000000000..0fdb8bb53f --- /dev/null +++ b/src/core/atlas/font-manager.js @@ -0,0 +1,227 @@ +import TinySDF from '@mapbox/tiny-sdf'; +import { buildMapping } from '../../../../util/font-util'; +import * as THREE from '../../../../core/three'; +import LRUCache from './lru-cache'; +export const DEFAULT_CHAR_SET = getDefaultCharacterSet(); +export const DEFAULT_FONT_FAMILY = 'sans-serif'; +export const DEFAULT_FONT_WEIGHT = 'normal'; +export const DEFAULT_FONT_SIZE = 24; +export const DEFAULT_BUFFER = 3; +export const DEFAULT_CUTOFF = 0.25; +export const DEFAULT_RADIUS = 8; +const MAX_CANVAS_WIDTH = 1024; +const BASELINE_SCALE = 0.9; +const HEIGHT_SCALE = 1.2; +const CACHE_LIMIT = 3; +const cache = new LRUCache(CACHE_LIMIT); + +const VALID_PROPS = [ + 'fontFamily', + 'fontWeight', + 'characterSet', + 'fontSize', + 'sdf', + 'buffer', + 'cutoff', + 'radius' +]; + +function getDefaultCharacterSet() { + const charSet = []; + for (let i = 32; i < 128; i++) { + charSet.push(String.fromCharCode(i)); + } + return charSet; +} + +function setTextStyle(ctx, fontFamily, fontSize, fontWeight) { + ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`; + ctx.fillStyle = '#000'; + ctx.textBaseline = 'baseline'; + ctx.textAlign = 'left'; +} +function getNewChars(key, characterSet) { + const cachedFontAtlas = cache.get(key); + if (!cachedFontAtlas) { + return characterSet; + } + + const newChars = []; + const cachedMapping = cachedFontAtlas.mapping; + let cachedCharSet = Object.keys(cachedMapping); + cachedCharSet = new Set(cachedCharSet); + + let charSet = characterSet; + if (charSet instanceof Array) { + charSet = new Set(charSet); + } + + charSet.forEach(char => { + if (!cachedCharSet.has(char)) { + newChars.push(char); + } + }); + + return newChars; +} + +function populateAlphaChannel(alphaChannel, imageData) { + // populate distance value from tinySDF to image alpha channel + for (let i = 0; i < alphaChannel.length; i++) { + imageData.data[4 * i + 3] = alphaChannel[i]; + } +} + +export default class FontAtlasManager { + constructor() { + + // font settings + this.props = { + fontFamily: DEFAULT_FONT_FAMILY, + fontWeight: DEFAULT_FONT_WEIGHT, + characterSet: DEFAULT_CHAR_SET, + fontSize: DEFAULT_FONT_SIZE, + buffer: DEFAULT_BUFFER, + // sdf only props + // https://github.com/mapbox/tiny-sdf + sdf: true, + cutoff: DEFAULT_CUTOFF, + radius: DEFAULT_RADIUS + }; + + // key is used for caching generated fontAtlas + this._key = null; + this._texture = new THREE.Texture(); + } + + get texture() { + return this._texture; + } + + get mapping() { + const data = cache.get(this._key); + return data && data.mapping; + } + + get scale() { + return HEIGHT_SCALE; + } + + get fontAtlas() { + return this._fontAtlas; + } + + setProps(props = {}) { + VALID_PROPS.forEach(prop => { + if (prop in props) { + this.props[prop] = props[prop]; + } + }); + + // update cache key + const oldKey = this._key; + this._key = this._getKey(); + + const charSet = getNewChars(this._key, this.props.characterSet); + const cachedFontAtlas = cache.get(this._key); + + // if a fontAtlas associated with the new settings is cached and + // there are no new chars + if (cachedFontAtlas && charSet.length === 0) { + // update texture with cached fontAtlas + if (this._key !== oldKey) { + this._updateTexture(cachedFontAtlas); + } + return; + } + + // update fontAtlas with new settings + const fontAtlas = this._generateFontAtlas(this._key, charSet, cachedFontAtlas); + this._fontAtlas = fontAtlas; + this._updateTexture(fontAtlas); + + // update cache + cache.set(this._key, fontAtlas); + } + + _updateTexture({ data: canvas }) { + this._texture = new THREE.CanvasTexture(canvas); + this._texture.wrapS = THREE.ClampToEdgeWrapping; + this._texture.wrapT = THREE.ClampToEdgeWrapping; + this._texture.minFilter = THREE.LinearFilter; + this._texture.flipY = false; + this._texture.needUpdate = true; + } + + _generateFontAtlas(key, characterSet, cachedFontAtlas) { + const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props; + let canvas = cachedFontAtlas && cachedFontAtlas.data; + if (!canvas) { + canvas = document.createElement('canvas'); + canvas.width = MAX_CANVAS_WIDTH; + } + const ctx = canvas.getContext('2d'); + + setTextStyle(ctx, fontFamily, fontSize, fontWeight); + + // 1. build mapping + const { mapping, canvasHeight, xOffset, yOffset } = buildMapping( + Object.assign( + { + getFontWidth: char => ctx.measureText(char).width, + fontHeight: fontSize * HEIGHT_SCALE, + buffer, + characterSet, + maxCanvasWidth: MAX_CANVAS_WIDTH + }, + cachedFontAtlas && { + mapping: cachedFontAtlas.mapping, + xOffset: cachedFontAtlas.xOffset, + yOffset: cachedFontAtlas.yOffset + } + ) + ); + + // 2. update canvas + // copy old canvas data to new canvas only when height changed + if (canvas.height !== canvasHeight) { + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + canvas.height = canvasHeight; + ctx.putImageData(imageData, 0, 0); + } + setTextStyle(ctx, fontFamily, fontSize, fontWeight); + + // 3. layout characters + if (sdf) { + const tinySDF = new TinySDF(fontSize, buffer, radius, cutoff, fontFamily, fontWeight); + // used to store distance values from tinySDF + // tinySDF.size equals `fontSize + buffer * 2` + const imageData = ctx.getImageData(0, 0, tinySDF.size, tinySDF.size); + + for (const char of characterSet) { + populateAlphaChannel(tinySDF.draw(char), imageData); + ctx.putImageData(imageData, mapping[char].x - buffer, mapping[char].y - buffer); + } + } else { + for (const char of characterSet) { + ctx.fillText(char, mapping[char].x, mapping[char].y + fontSize * BASELINE_SCALE); + } + } + return { + xOffset, + yOffset, + mapping, + data: canvas, + width: canvas.width, + height: canvas.height + }; + } + + _getKey() { + const { fontFamily, fontWeight, fontSize, buffer, sdf, radius, cutoff } = this.props; + if (sdf) { + return `${fontFamily} ${fontWeight} ${fontSize} ${buffer} ${radius} ${cutoff}`; + } + return `${fontFamily} ${fontWeight} ${fontSize} ${buffer}`; + } +} diff --git a/src/core/atlas/icon-manager.js b/src/core/atlas/icon-manager.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/core/atlas/lru-cache.js b/src/core/atlas/lru-cache.js new file mode 100644 index 0000000000..8b417e53e2 --- /dev/null +++ b/src/core/atlas/lru-cache.js @@ -0,0 +1,71 @@ +/** + * LRU Cache class with limit + * + * Update order for each get/set operation + * Delete oldest when reach given limit + */ + +export default class LRUCache { + constructor(limit = 5) { + this.limit = limit; + + this.clear(); + } + + clear() { + this._cache = {}; + // access/update order, first item is oldest, last item is newest + this._order = []; + } + + get(key) { + const value = this._cache[key]; + if (value) { + // update order + this._deleteOrder(key); + this._appendOrder(key); + } + return value; + } + + set(key, value) { + if (!this._cache[key]) { + // if reach limit, delete the oldest + if (Object.keys(this._cache).length === this.limit) { + this.delete(this._order[0]); + } + + this._cache[key] = value; + this._appendOrder(key); + } else { + // if found in cache, delete the old one, insert new one to the first of list + this.delete(key); + + this._cache[key] = value; + this._appendOrder(key); + } + } + + delete(key) { + const value = this._cache[key]; + if (value) { + this._deleteCache(key); + this._deleteOrder(key); + } + } + + _deleteCache(key) { + delete this._cache[key]; + } + + _deleteOrder(key) { + const index = this._order.findIndex(o => o === key); + if (index >= 0) { + this._order.splice(index, 1); + } + } + + _appendOrder(key) { + this._order.push(key); + } +} diff --git a/src/core/layer.js b/src/core/layer.js index 1403fee10e..6b974bddbc 100644 --- a/src/core/layer.js +++ b/src/core/layer.js @@ -8,7 +8,7 @@ import ColorUtil from '../attr/color-util'; import Controller from './controller/index'; import source from './source'; import pickingFragmentShader from '../core/engine/picking/picking_frag.glsl'; -// import PickingMaterial from '../core/engine/picking/pickingMaterial'; +import { getInteraction } from '../interaction/index'; import Attr from '../attr/index'; import Util from '../util'; import Global from '../global'; @@ -53,6 +53,7 @@ export default class Layer extends Base { activedOptions: { fill: [ 1.0, 0, 0, 1.0 ] }, + interactions: {}, animateOptions: { enable: false } @@ -66,6 +67,7 @@ export default class Layer extends Base { this._pickObject3D = new THREE.Object3D(); this._object3D.visible = this.get('visible'); this._object3D.renderOrder = this.get('zIndex') || 0; + this._mapEventHandlers = []; const layerId = this._getUniqueId(); this.layerId = layerId; this._activeIds = null; @@ -88,12 +90,8 @@ export default class Layer extends Base { this.scene._engine.composerLayers.push(object); return; } - type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object; this._visibleWithZoom(); - this._zoomchangeHander = this._visibleWithZoom.bind(this); - this.scene.on('zoomchange', this._zoomchangeHander); - object.onBeforeRender = () => { // 每次渲染前改变状态 const zoom = this.scene.getZoom(); object.material.setUniformsValue('u_time', this.scene._engine.clock.getElapsedTime()); @@ -113,7 +111,7 @@ export default class Layer extends Base { this._addPickMesh(object);// 不对边界线进行拾取 } this.scene._engine.update(); - setTimeout(() => this.scene._engine.update(), 500); + setTimeout(() => this.scene._engine.update(), 200); } remove(object) { if (object.type === 'composer') { @@ -132,8 +130,13 @@ export default class Layer extends Base { this._object3D.visible = this.get('visible'); } source(data, cfg = {}) { + if (data instanceof source) { + this.layerSource = data; + return this; + } cfg.data = data; cfg.mapType = this.scene.mapType; + cfg.zoom = this.scene.getZoom(); this.layerSource = new source(cfg); // this.scene.workerPool.runTask({ // command: 'geojson', @@ -263,6 +266,10 @@ export default class Layer extends Base { this._visible(true); return this; } + setData(data, cfg) { + this.layerSource.setData(data, cfg); + this.repaint(); + } _createScale(field) { // TODO scale更新 const scales = this.get('scales'); @@ -316,25 +323,53 @@ export default class Layer extends Base { } return scale; } + // 重绘 度量, 映射,顶点构建 + repaint() { + this.set('scales', {}); + this._initControllers(); + this._initAttrs(); + this._mapping(); + this.redraw(); + } // 初始化图层 init() { this._initControllers(); this._initAttrs(); this._scaleByZoom(); + this._initInteraction(); + this._initMapEvent(); + this._mapping(); - - const activeHander = this._addActiveFeature.bind(this); - const resetHander = this._resetStyle.bind(this); + } + _initInteraction() { if (this.get('allowActive')) { - - this.on('mousemove', activeHander); - this.on('mouseleave', resetHander); - - } else { - this.off('mousemove', activeHander); - this.off('mouseleave', resetHander); + this.interaction('active'); } } + _initMapEvent() { + // zoomchange mapmove resize + const EVENT_TYPES = [ 'zoomchange', 'dragend' ]; + Util.each(EVENT_TYPES, type => { + const handler = Util.wrapBehavior(this, `${type}`); + this.map.on(`${type}`, handler); + this._mapEventHandlers.push({ type, handler }); + }); + } + clearMapEvent() { + const eventHandlers = this._mapEventHandlers; + Util.each(eventHandlers, eh => { + this.map.off(eh.type, eh.handler); + }); + } + zoomchange(ev) { + // 地图缩放等级变化 + this._visibleWithZoom(ev); + } + dragend() { + + } + resize() { + } setActive(id, color) { this._activeIds = id; @@ -483,17 +518,6 @@ export default class Layer extends Base { const values = attr.mapping(...params); return values; } - - // temp - _getDataType(data) { - if (data.hasOwnProperty('type')) { - const type = data.type; - if (type === 'FeatureCollection') { - return 'geojson'; - } - } - return 'basic'; - } _scaleByZoom() { if (this._zoomScale) { this.map.on('zoomend', () => { @@ -502,11 +526,7 @@ export default class Layer extends Base { }); } } - // on(type, callback) { - // this._addPickingEvents(); - // super.on(type, callback); - // } getPickingId() { return this.scene._engine._picking.getNextId(); } @@ -519,19 +539,9 @@ export default class Layer extends Base { _addPickMesh(mesh) { this._pickingMesh = new THREE.Object3D(); this._pickingMesh.name = this.layerId; - // this._visibleWithZoom(); - // this.scene.on('zoomchange', () => { - // this._visibleWithZoom(); - // }); this.addToPicking(this._pickingMesh); const pickmaterial = mesh.material.clone(); - pickmaterial.fragmentShader = pickingFragmentShader; - // const pickmaterial = new PickingMaterial({ - // u_zoom: this.scene.getZoom(), - // vs: mesh.material. - // }); - const pickingMesh = new THREE[mesh.type](mesh.geometry, pickmaterial); pickingMesh.name = this.layerId; pickmaterial.setDefinesvalue(this.type, true); @@ -542,9 +552,6 @@ export default class Layer extends Base { this._pickingMesh.add(pickingMesh); } - _setPickingId() { - this._pickingId = this.getPickingId(); - } _initEvents() { this.scene.on('pick-' + this.layerId, e => { let { featureId, point2d, type } = e; @@ -552,6 +559,7 @@ export default class Layer extends Base { type = 'mouseleave'; // featureId = this._activeIds; } + this._activeIds = featureId; const feature = this.layerSource.getSelectFeature(featureId); const lnglat = this.scene.containerToLngLat(point2d); const style = this.layerData[featureId - 1]; @@ -569,39 +577,6 @@ export default class Layer extends Base { }); } - /** - * 更新active操作 - * @param {*} featureStyleId 需要更新的要素Id - * @param {*} style 更新的要素样式 - */ - updateStyle(featureStyleId, style) { - if (this._activeIds) { - this._resetStyle(); - } - this._activeIds = featureStyleId; - const pickingId = this.layerMesh.geometry.attributes.pickingId.array; - const color = style.color; - const colorAttr = this.layerMesh.geometry.attributes.a_color; - const firstId = pickingId.indexOf(featureStyleId[0]); - for (let i = firstId; i < pickingId.length; i++) { - if (pickingId[i] === featureStyleId[0]) { - colorAttr.array[i * 4 + 0] = color[0]; - colorAttr.array[i * 4 + 1] = color[1]; - colorAttr.array[i * 4 + 2] = color[2]; - colorAttr.array[i * 4 + 3] = color[3]; - } else { - break; - } - } - colorAttr.needsUpdate = true; - return; - } - - _updateColor() { - - this._updateMaping(); - - } /** * 用于过滤数据 * @param {*} object 需要过滤的mesh @@ -642,6 +617,8 @@ export default class Layer extends Base { let offset = 0; if (this.type === 'point') { offset = 5; + this.shapeType = 'text' && (offset = 10); + } else if (this.type === 'polyline') { offset = 2; } @@ -652,6 +629,51 @@ export default class Layer extends Base { this._object3D.visible = true; } } + // 重新构建mesh + redraw() { + this._object3D.children.forEach(child => { + this._object3D.remove(child); + }); + this.removeFromPicking(this._pickingMesh); + this.draw(); + } + // 更新mesh + updateDraw() { + + } + + // interaction 方法 + clearAllInteractions() { + const interactions = this.get('interactions'); + Util.each(interactions, (interaction, key) => { + interaction.destory(); + delete interactions[key]; + }); + return this; + } + clearInteraction(type) { + const interactions = this.get('interactions'); + if (interactions[type]) { + interactions[type].destory(); + delete interactions[type]; + } + return this; + } + interaction(type, cfg = {}) { + cfg.layer = this; + const Ctor = getInteraction(type); + const interaction = new Ctor(cfg); + this._setInteraction(type, interaction); + return this; + } + _setInteraction(type, interaction) { + const interactions = this.get('interactions'); + if (interactions[type]) { + interactions[type].destory(); + } + interactions[type] = interaction; + } + /** * 重置高亮要素 */ @@ -664,6 +686,8 @@ export default class Layer extends Base { */ destroy() { this.removeAllListeners(); + this.clearAllInteractions(); + this.clearMapEvent(); if (this._object3D.type === 'composer') { this.remove(this._object3D); @@ -696,7 +720,6 @@ export default class Layer extends Base { this._object3D = null; this.scene._engine._scene.remove(this._object3D); this.scene._engine._picking.remove(this._pickingMesh); - this.scene.off('zoomchange', this._zoomchangeHander); this.destroyed = true; } diff --git a/src/core/scene.js b/src/core/scene.js index 5d22c4e67d..e662d45b9f 100644 --- a/src/core/scene.js +++ b/src/core/scene.js @@ -69,14 +69,6 @@ export default class Scene extends Base { super.off(type, hander); } - _initAttribution() { - const message = 'AntV | L7 '; - const element = document.createElement('div'); - - element.innerHTML = message; - element.style.cssText += 'position: absolute; pointer-events:none;background: rgba(255, 255, 255, 0.7);font-size: 11px;z-index:100; padding:4px;bottom: 0;right:0px;'; - this._container.appendChild(element); - } addImage() { this.image = new LoadImage(); } diff --git a/src/core/source.js b/src/core/source.js index 305e535178..25841350ea 100644 --- a/src/core/source.js +++ b/src/core/source.js @@ -1,6 +1,8 @@ import Base from './base'; import { getTransform, getParser } from '../source'; +import { cluster, formatData } from '../source/transform/cluster'; import { extent, tranfrormCoord } from '../util/geo'; +import { clone } from '@antv/util'; import { getMap } from '../map/index'; export default class Source extends Base { getDefaultCfg() { @@ -21,18 +23,40 @@ export default class Source extends Base { const mapType = this.get('mapType'); this.projectFlat = getMap(mapType).project; // 数据解析 + this._init(); + + } + _init() { this._excuteParser(); + const isCluster = this.get('isCluster') || false; + isCluster && this._executeCluster(); // 数据转换 统计,聚合,分类 this._executeTrans(); // 坐标转换 this._projectCoords(); - } + setData(data, cfg = {}) { + Object.assign(this._attrs, cfg); + const transform = this.get('transforms'); + this._transforms = transform || []; + this.set('data', data); + this._init(); + } + // 数据更新 + updateTransfrom(cfg) { + const { transforms } = cfg; + this._transforms = transforms; + this.data = clone(this.originData); + this._executeTrans(); + this._projectCoords(); + } + _excuteParser() { const parser = this.get('parser'); const { type = 'geojson' } = parser; const data = this.get('data'); - this.data = getParser(type)(data, parser); + this.originData = getParser(type)(data, parser); + this.data = clone(this.originData); this.data.extent = extent(this.data.dataArray); } /** @@ -51,6 +75,24 @@ export default class Source extends Base { const data = getTransform(option.type)(this.data, option); Object.assign(this.data, data); } + _executeCluster() { + const clusterCfg = this.get('Cluster') || {}; + const zoom = this.get('zoom'); + clusterCfg.zoom = Math.floor(zoom); + this.set('cluster', clusterCfg); + const clusterData = cluster(this.data, clusterCfg); + this.data = clusterData.data; + this.pointIndex = clusterData.pointIndex; + } + updateCusterData(zoom, bbox) { + const clusterPoint = this.pointIndex.getClusters(bbox, zoom); + this.data.dataArray = formatData(clusterPoint); + const clusterCfg = this.get('Cluster') || {}; + clusterCfg.zoom = Math.floor(zoom); + clusterCfg.bbox = bbox; + this.set('cluster', clusterCfg); + this._projectCoords(); + } _projectCoords() { this.data.dataArray.forEach(data => { // data.coordinates = this._coordProject(data.coordinates); diff --git a/src/geom/base.js b/src/geom/base.js new file mode 100644 index 0000000000..9e1488b9df --- /dev/null +++ b/src/geom/base.js @@ -0,0 +1,21 @@ +import Base from '../core/base'; +export class GeomBase extends Base { + geometryBuffer() { + + } + geometry() { + } + + material() { + + } + + drawMesh() { + + } + + +} + + +export default GeomBase; diff --git a/src/geom/buffer/point/text.js b/src/geom/buffer/point/text.js index 2552bdf894..9b2f5f50b8 100644 --- a/src/geom/buffer/point/text.js +++ b/src/geom/buffer/point/text.js @@ -29,12 +29,11 @@ function drawGlyph(layerData, fontAtlasManager) { const size = element.size; const pos = element.coordinates; let text = element.shape || ''; + text = text.toString(); const pen = { x: (-text.length * size) / 2, y: 0 }; - text = text.toString(); - for (let i = 0; i < text.length; i++) { const metric = mapping[text[i]]; const { x, y, width, height } = metric; diff --git a/src/geom/geom.js b/src/geom/geom.js deleted file mode 100644 index 9684109ea2..0000000000 --- a/src/geom/geom.js +++ /dev/null @@ -1,62 +0,0 @@ -const geom = { - point: { - symbol: [ 'circle', 'hexagon', 'triangle', 'diamond' ], - native: { - buffer: '', - geometry: 'PointGeometry', - material: 'PointMaterial' - }, - line: { - buffer: 'PointBuffer', - geometry: 'PolygonLine', - material: 'MeshlineMaterial' - }, - fill: { - buffer: 'PointBuffer', - geometry: 'PolygonGeometry', - material: 'PolygonMaterial' - }, - extrude: { - buffer: 'PointBuffer', - geometry: 'PolygonGeometry', - material: 'PolygonMaterial' - }, - extrudeline: { - buffer: 'PointBuffer', - geometry: 'PolygonLine', - material: 'MeshlineMaterial' - }, - pointGrid: { - buffer: 'pointGrid', - geometry: 'PolygonLine', - material: 'MeshlineMaterial' - - } - }, - line: { - shape: [ 'native' ] - }, - polygon: { - line: { - buffer: 'polygonLineBuffer', - geometry: 'PolygonLine', - material: 'MeshlineMaterial' - }, - fill: { - buffer: 'PolygonBuffer', - geometry: 'PolygonGeometry', - material: 'PolygonMaterial' - }, - extrude: { - buffer: 'PolygonBuffer', - geometry: 'PolygonGeometry', - material: 'PolygonMaterial' - }, - extrudeline: { - buffer: 'polygonLineBuffer', - geometry: 'PolygonLine', - material: 'MeshlineMaterial' - } - } -}; -export default geom; diff --git a/src/geom/shader/point_meshLine_vert.glsl b/src/geom/shader/point_meshLine_vert.glsl index 3493808811..d4773d23e6 100644 --- a/src/geom/shader/point_meshLine_vert.glsl +++ b/src/geom/shader/point_meshLine_vert.glsl @@ -15,7 +15,7 @@ varying vec4 v_color; void main() { mat4 matModelViewProjection = projectionMatrix * modelViewMatrix; float scale = pow(2.0,(20.0 - u_zoom)); - vec3 newposition = position + (a_size + vec3(u_strokeWidth/2.,u_strokeWidth/2.,0)) * scale* a_shape; + vec3 newposition = position + (a_size + vec3(u_strokeWidth/2.,u_strokeWidth/2.,0)) * scale* a_shape + vec3(0., a_size.y * scale / 4., 0.);; #ifdef ANIMATE vTime = 1.0- (mod(u_time*50.,3600.)- position.z) / 100.; #endif diff --git a/src/geom/shader/polygon_vert.glsl b/src/geom/shader/polygon_vert.glsl index 36474bde79..2015bee5f5 100644 --- a/src/geom/shader/polygon_vert.glsl +++ b/src/geom/shader/polygon_vert.glsl @@ -21,7 +21,7 @@ void main() { vec3 newposition = position; // newposition.x -= 128.0; #ifdef SHAPE - newposition =position + a_size * scale* a_shape; + newposition =position + a_size * scale* a_shape + vec3(0., a_size.y * scale / 4., 0.); #endif v_texCoord = faceUv; if(normal == vec3(0.,0.,1.)){ diff --git a/src/geom/shader/text_frag.glsl b/src/geom/shader/text_frag.glsl index f29d97a435..7fe59c4257 100644 --- a/src/geom/shader/text_frag.glsl +++ b/src/geom/shader/text_frag.glsl @@ -7,9 +7,11 @@ uniform float u_buffer; uniform float u_gamma; uniform float u_opacity; varying vec2 v_texcoord; +varying float v_size; void main(){ float dist=texture2D(u_texture,vec2(v_texcoord.x,v_texcoord.y)).a; float alpha; + if(u_strokeWidth==0.){ alpha=smoothstep(u_buffer-u_gamma,u_buffer+u_gamma,dist); gl_FragColor=vec4(v_color.rgb,alpha*v_color.a); diff --git a/src/geom/shader/text_vert.glsl b/src/geom/shader/text_vert.glsl index 51ff8a0150..f4290df289 100644 --- a/src/geom/shader/text_vert.glsl +++ b/src/geom/shader/text_vert.glsl @@ -7,12 +7,14 @@ uniform vec2 u_textTextureSize;// 纹理大小 uniform vec2 u_glSize; varying vec2 v_texcoord; varying vec4 v_color; +varying float v_size; uniform float u_activeId; uniform vec4 u_activeColor; void main(){ mat4 matModelViewProjection=projectionMatrix*modelViewMatrix; vec4 cur_position=matModelViewProjection*vec4(position.xy,0,1); + v_size = 12. / u_glSize.x; gl_Position=cur_position/cur_position.w+vec4((a_txtOffsets+a_txtsize)/u_glSize*2.,0.,0.); v_color=vec4(a_color.rgb,a_color.a*u_opacity); if(pickingId==u_activeId){ diff --git a/src/geom/shape/path.js b/src/geom/shape/path.js index d4c2842a50..a693484d9c 100644 --- a/src/geom/shape/path.js +++ b/src/geom/shape/path.js @@ -33,7 +33,7 @@ export function polygonPath(pointCount) { const step = Math.PI * 2 / pointCount; const line = []; for (let i = 0; i < pointCount; i++) { - line.push(step * i); + line.push(step * i - Math.PI / 12); } const path = line.map(t => { const x = Math.sin(t + Math.PI / 4), diff --git a/src/index.js b/src/index.js index f791d2df22..399f8df9ec 100755 --- a/src/index.js +++ b/src/index.js @@ -2,10 +2,19 @@ // import Util from './util'; import Scene from './core/scene'; import Global from './global'; - +import Source from './core/source'; +import { registerParser, registerTransform } from './source'; +import { registerInteraction, getInteraction } from './interaction'; +import { registerLayer } from './layer'; const version = Global.version; export { version, - Scene + Scene, + Source, + registerParser, + registerTransform, + registerLayer, + registerInteraction, + getInteraction }; diff --git a/src/interaction/active.js b/src/interaction/active.js new file mode 100644 index 0000000000..d4e8807d45 --- /dev/null +++ b/src/interaction/active.js @@ -0,0 +1,16 @@ +import Interaction from './base'; +export default class Active extends Interaction { + constructor(cfg) { + super({ + processEvent: 'mousemove', + resetEvent: 'mouseleave', + ...cfg + }); + } + process(ev) { + this.layer._addActiveFeature(ev); + } + reset() { + this.layer._resetStyle(); + } +} diff --git a/src/interaction/base.js b/src/interaction/base.js new file mode 100644 index 0000000000..2103c1a5cd --- /dev/null +++ b/src/interaction/base.js @@ -0,0 +1,87 @@ +import * as _ from '@antv/util'; +const EVENT_TYPES = [ 'start', 'process', 'end', 'reset' ]; + +export default class Interaction { + constructor(cfg) { + const defaultCfg = this._getDefaultCfg(); + Object.assign(this, defaultCfg, cfg); + this._eventHandlers = []; + this._bindEvents(); + } + _getDefaultCfg() { + return { + startEvent: 'mousedown', + processEvent: 'mousemove', + endEvent: 'mouseup', + resetEvent: 'dblclick' + }; + } + _start(ev) { + this.preStart(ev); + this.start(ev); + this.afterStart(ev); + } + + preStart() {} + + start() {} + + afterStart() {} + + _process(ev) { + this.preProcess(ev); + this.process(ev); + this.afterProcess(ev); + } + + preProcess() {} + + process() { + } + + afterProcess() {} + + _end(ev) { + this.preEnd(ev); + this.end(ev); + this.afterEnd(ev); + } + preEnd() {} + + end() {} + + afterEnd() {} + + _reset() { + this.preReset(); + this.reset(); + this.afterReset(); + } + + preReset() {} + + reset() {} + + afterReset() {} + + _bindEvents() { + _.each(EVENT_TYPES, type => { + const eventName = this[`${type}Event`]; + const handler = _.wrapBehavior(this, `_${type}`); + this.layer.on(eventName, handler); + this._eventHandlers.push({ type: eventName, handler }); + }); + } + + _unbindEvents() { + const eventHandlers = this._eventHandlers; + _.each(eventHandlers, eh => { + this.layer.off(eh.type, eh.handler); + }); + } + + destory() { + this._unbindEvents(); + this._reset(); + } +} diff --git a/src/interaction/factory.js b/src/interaction/factory.js new file mode 100644 index 0000000000..4442e4b7e9 --- /dev/null +++ b/src/interaction/factory.js @@ -0,0 +1,14 @@ +export const INTERACTION_MAP = {}; + +export const getInteraction = type => { + return INTERACTION_MAP[type]; +}; + +export const registerInteraction = (type, ctor) => { + // 注册的时候,需要校验 type 重名,不区分大小写 + if (getInteraction(type)) { + throw new Error(`Interaction type '${type}' existed.`); + } + // 存储到 map 中 + INTERACTION_MAP[type] = ctor; +}; diff --git a/src/interaction/index.js b/src/interaction/index.js new file mode 100644 index 0000000000..838f27ddac --- /dev/null +++ b/src/interaction/index.js @@ -0,0 +1,9 @@ +import Interaction from './base'; +import Active from './active'; +import Select from './select'; +import { getInteraction, registerInteraction } from './factory'; + +registerInteraction('active', Active); +registerInteraction('select', Select); + +export { Interaction, registerInteraction, getInteraction }; diff --git a/src/interaction/select.js b/src/interaction/select.js new file mode 100644 index 0000000000..2cf5e4d001 --- /dev/null +++ b/src/interaction/select.js @@ -0,0 +1,12 @@ +import Interaction from './base'; +export default class Select extends Interaction { + constructor(cfg) { + super({ + processEvent: 'click', + ...cfg + }); + } + process(ev) { + this.layer._addActiveFeature(ev); + } +} diff --git a/src/layer/heatmapLayer.js b/src/layer/heatmapLayer.js index ae8e34b1df..1c729001df 100644 --- a/src/layer/heatmapLayer.js +++ b/src/layer/heatmapLayer.js @@ -11,10 +11,10 @@ export default class HeatMapLayer extends Layer { return this; } render() { - this._prepareRender(); + this.draw(); return this; } - _prepareRender() { + draw() { this.init(); this.type = 'heatmap'; switch (this.shapeType) { @@ -56,10 +56,4 @@ export default class HeatMapLayer extends Layer { this.add(girdMesh); } - // afterRender() { - // if (this.shapeType !== 'grid' && this.shapeType !== 'hexagon') { - // updateIntensityPass(this); - // } - // } - } diff --git a/src/layer/index.js b/src/layer/index.js index f7754776e2..02e72edceb 100644 --- a/src/layer/index.js +++ b/src/layer/index.js @@ -14,4 +14,5 @@ registerLayer('RasterLayer', RasterLayer); registerLayer('HeatmapLayer', HeatmapLayer); export { LAYER_MAP } from './factory'; +export { registerLayer }; diff --git a/src/layer/lineLayer.js b/src/layer/lineLayer.js index b31f8e96fa..bd039bc178 100644 --- a/src/layer/lineLayer.js +++ b/src/layer/lineLayer.js @@ -1,7 +1,12 @@ import Layer from '../core/layer'; import * as THREE from '../core/three'; import { LineBuffer } from '../geom/buffer/index'; -import { LineMaterial, ArcLineMaterial, MeshLineMaterial, DashLineMaterial } from '../geom/material/lineMaterial'; +import { + LineMaterial, + ArcLineMaterial, + MeshLineMaterial, + DashLineMaterial +} from '../geom/material/lineMaterial'; export default class LineLayer extends Layer { shape(type) { this.shapeType = type; @@ -10,26 +15,60 @@ export default class LineLayer extends Layer { render() { this.type = 'polyline'; this.init(); + this.draw(); + return this; + } + preRender() { + if ( + this.animateDuration > 0 && + this.animateDuration < this.scene._engine.clock.getElapsedTime() + ) { + this.layerMesh.material.setDefinesvalue('ANIMATE', false); + this.emit('animateEnd'); + this.scene.stopAnimate(); + this.animateDuration = Infinity; + } + } + draw() { const layerData = this.layerData; const style = this.get('styleOptions'); - const buffer = this._buffer = new LineBuffer({ + const buffer = (this._buffer = new LineBuffer({ layerData, shapeType: this.shapeType, style - }); + })); const { opacity } = this.get('styleOptions'); const animateOptions = this.get('animateOptions'); const activeOption = this.get('activedOptions'); + // const layerCfg = { + // ...style, + // ...animateOptions, + // ...activeOption + // }; const geometry = new THREE.BufferGeometry(); const { attributes } = buffer; - if (this.shapeType === 'arc') { geometry.setIndex(attributes.indexArray); - geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1)); - geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.positions, 3)); - geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4)); - geometry.addAttribute('a_instance', new THREE.Float32BufferAttribute(attributes.instances, 4)); - geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1)); + geometry.addAttribute( + 'pickingId', + new THREE.Float32BufferAttribute(attributes.pickingIds, 1) + ); + geometry.addAttribute( + 'position', + new THREE.Float32BufferAttribute(attributes.positions, 3) + ); + geometry.addAttribute( + 'a_color', + new THREE.Float32BufferAttribute(attributes.colors, 4) + ); + geometry.addAttribute( + 'a_instance', + new THREE.Float32BufferAttribute(attributes.instances, 4) + ); + geometry.addAttribute( + 'a_size', + new THREE.Float32BufferAttribute(attributes.sizes, 1) + ); const material = new ArcLineMaterial({ u_opacity: opacity, u_zoom: this.scene.getZoom(), @@ -38,20 +77,40 @@ export default class LineLayer extends Layer { const mesh = new THREE.Mesh(geometry, material); this.add(mesh); } else if (this.shapeType === 'line') { - + // DrawLine(attributes, layerCfg) geometry.setIndex(attributes.indexArray); - geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1)); - geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.positions, 3)); - geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4)); - geometry.addAttribute('a_size', new THREE.Float32BufferAttribute(attributes.sizes, 1)); - geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normal, 3)); - geometry.addAttribute('a_miter', new THREE.Float32BufferAttribute(attributes.miter, 1)); - geometry.addAttribute('a_distance', new THREE.Float32BufferAttribute(attributes.attrDistance, 1)); + geometry.addAttribute( + 'pickingId', + new THREE.Float32BufferAttribute(attributes.pickingIds, 1) + ); + geometry.addAttribute( + 'position', + new THREE.Float32BufferAttribute(attributes.positions, 3) + ); + geometry.addAttribute( + 'a_color', + new THREE.Float32BufferAttribute(attributes.colors, 4) + ); + geometry.addAttribute( + 'a_size', + new THREE.Float32BufferAttribute(attributes.sizes, 1) + ); + geometry.addAttribute( + 'normal', + new THREE.Float32BufferAttribute(attributes.normal, 3) + ); + geometry.addAttribute( + 'a_miter', + new THREE.Float32BufferAttribute(attributes.miter, 1) + ); + geometry.addAttribute( + 'a_distance', + new THREE.Float32BufferAttribute(attributes.attrDistance, 1) + ); const lineType = style.lineType; let material; if (lineType !== 'dash') { - material = new MeshLineMaterial({ u_opacity: opacity, u_zoom: this.scene.getZoom(), @@ -59,21 +118,27 @@ export default class LineLayer extends Layer { }); if (animateOptions.enable) { - material.setDefinesvalue('ANIMATE', true); this.scene.startAnimate(); - const { duration, interval, trailLength, repeat = Infinity } = animateOptions; - this.animateDuration = this.scene._engine.clock.getElapsedTime() + duration * repeat; + const { + duration, + interval, + trailLength, + repeat = Infinity + } = animateOptions; + this.animateDuration = + this.scene._engine.clock.getElapsedTime() + duration * repeat; material.upDateUninform({ u_duration: duration, u_interval: interval, u_trailLength: trailLength }); - - } } else { - geometry.addAttribute('a_distance', new THREE.Float32BufferAttribute(attributes.attrDistance, 1)); + geometry.addAttribute( + 'a_distance', + new THREE.Float32BufferAttribute(attributes.attrDistance, 1) + ); material = new DashLineMaterial({ u_opacity: opacity, u_zoom: this.scene.getZoom(), @@ -82,10 +147,20 @@ export default class LineLayer extends Layer { } const mesh = new THREE.Mesh(geometry, material); this.add(mesh); - } else { // 直线 - geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1)); - geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3)); - geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4)); + } else { + // 直线 + geometry.addAttribute( + 'pickingId', + new THREE.Float32BufferAttribute(attributes.pickingIds, 1) + ); + geometry.addAttribute( + 'position', + new THREE.Float32BufferAttribute(attributes.vertices, 3) + ); + geometry.addAttribute( + 'a_color', + new THREE.Float32BufferAttribute(attributes.colors, 4) + ); const material = new LineMaterial({ u_opacity: opacity, u_time: 0, @@ -99,14 +174,5 @@ export default class LineLayer extends Layer { const mesh = new THREE.LineSegments(geometry, material); this.add(mesh); } - return this; - } - preRender() { - if (this.animateDuration > 0 && this.animateDuration < this.scene._engine.clock.getElapsedTime()) { - this.layerMesh.material.setDefinesvalue('ANIMATE', false); - this.emit('animateEnd'); - this.scene.stopAnimate(); - this.animateDuration = Infinity; - } } } diff --git a/src/layer/pointLayer.js b/src/layer/pointLayer.js index de3cbf3a57..922830f94b 100644 --- a/src/layer/pointLayer.js +++ b/src/layer/pointLayer.js @@ -18,7 +18,7 @@ export default class PointLayer extends Layer { this.type = 'point'; this.init(); if (!this._hasRender) { - this._prepareRender(this.shapeType); + this.draw(); this._hasRender = true; } else { this._initAttrs(); @@ -28,7 +28,7 @@ export default class PointLayer extends Layer { } return this; } - _prepareRender() { + draw() { const { stroke, fill } = this.get('styleOptions'); const style = this.get('styleOptions'); const activeOption = this.get('activedOptions'); @@ -37,6 +37,7 @@ export default class PointLayer extends Layer { activeColor: activeOption.fill }; const pointShapeType = this._getShape(); + this.shapeType = pointShapeType; switch (pointShapeType) { case 'fill': { // 填充图形 if (fill !== 'none') { @@ -112,4 +113,33 @@ export default class PointLayer extends Layer { } return 'text'; } + zoomchange(ev) { + super.zoomchange(ev); + this._updateData(); + } + dragend(ev) { + super.dragend(ev); + this._updateData(); + + } + _updateData() { + if (this.layerSource.get('isCluster')) { + const bounds = this.scene.getBounds().toBounds(); + const SW = bounds.getSouthWest(); + const NE = bounds.getNorthEast(); + const zoom = this.scene.getZoom(); + const step = Math.max(NE.lng - SW.lng, NE.lat - SW.lat) / 2; + const bbox = [ SW.lng, SW.lat, NE.lng, NE.lat ]; + // const bbox = [ SW.lng - step, SW.lat - step, NE.lng + step, NE.lat + step ]; + const cfg = this.layerSource.get('cluster'); + const preBox = cfg.bbox; + const preZoom = cfg.zoom; + if (!(preBox && preBox[0] < bbox[0] && preBox[1] < bbox[1] && preBox[2] > bbox[2] && preBox[3] < bbox[3] && // 当前范围在范围内 + (Math.abs(zoom - preZoom)) < 1)) { + const newbbox = [ SW.lng - step, SW.lat - step, NE.lng + step, NE.lat + step ]; + this.layerSource.updateCusterData(Math.floor(zoom), newbbox); + this.repaint(); + } + } + } } diff --git a/src/layer/polygonLayer.js b/src/layer/polygonLayer.js index 17c95244ef..267e29cc9b 100644 --- a/src/layer/polygonLayer.js +++ b/src/layer/polygonLayer.js @@ -9,7 +9,7 @@ export default class PolygonLayer extends Layer { render() { if (!this._hasRender) { // 首次渲染 this._hasRender = true; - this._prepareRender(); + this.draw(); } else { this._initAttrs(); @@ -18,7 +18,7 @@ export default class PolygonLayer extends Layer { } return this; } - _prepareRender() { + draw() { this.init(); this.type = 'polygon'; this._buffer = new PolygonBuffer({ @@ -29,7 +29,6 @@ export default class PolygonLayer extends Layer { } update() { this.updateFilter(this.layerMesh); - // 动态更新相关属性 } _getLayerRender() { const animateOptions = this.get('animateOptions'); diff --git a/src/layer/render/line/drawLine.js b/src/layer/render/line/drawLine.js index e0a6f7a228..c509db47f4 100644 --- a/src/layer/render/line/drawLine.js +++ b/src/layer/render/line/drawLine.js @@ -23,5 +23,21 @@ export default function DrawLine(attributes, style) { ANIMATE: animate }); const arcMesh = new THREE.Mesh(geometry, lineMaterial); + if (animate) { + this.scene.startAnimate(); + const { + duration, + interval, + trailLength, + repeat = Infinity + } = style; + this.animateDuration = + this.scene._engine.clock.getElapsedTime() + duration * repeat; + lineMaterial.upDateUninform({ + u_duration: duration, + u_interval: interval, + u_trailLength: trailLength + }); + } return arcMesh; } diff --git a/src/layer/render/point/drawText.js b/src/layer/render/point/drawText.js index 0604938e61..32fa9a28d5 100644 --- a/src/layer/render/point/drawText.js +++ b/src/layer/render/point/drawText.js @@ -37,7 +37,7 @@ export default function DrawText(attributes, style) { attributes.fontAtlas.width, attributes.fontAtlas.height ], - u_gamma: 0.2, + u_gamma: (1.0 / 12.0) * (1.4142135623730951 / (2.0)), u_buffer: 0.75, u_opacity: opacity, u_glSize: [ width, height ], diff --git a/src/map/AMap.js b/src/map/AMap.js index 29374ad778..b5666bee4a 100644 --- a/src/map/AMap.js +++ b/src/map/AMap.js @@ -128,6 +128,9 @@ export default class GaodeMap extends Base { scene.setZoom = zoom => { return map.setZoom(zoom); }; + scene.getBounds = () => { + return map.getBounds(); + }; scene.setZoomAndCenter = (zoom, center) => { const lnglat = new AMap.LngLat(center[0], center[1]); return map.setZoomAndCenter(zoom, lnglat); diff --git a/src/source/transform/cluster.js b/src/source/transform/cluster.js index 645c7abcc0..4d87f3f360 100644 --- a/src/source/transform/cluster.js +++ b/src/source/transform/cluster.js @@ -38,10 +38,9 @@ export function cluster(data, option) { }; }); data.dataArray = resultData; - data.pointIndex = pointIndex; - return data; + return { data, pointIndex }; } -function formatData(clusterPoint) { +export function formatData(clusterPoint) { return clusterPoint.map((point, index) => { return { coordinates: point.geometry.coordinates, diff --git a/test/unit/source/transfrom/cluster-spec.js b/test/unit/source/transfrom/cluster-spec.js index f46ed7c23a..bd37f2a145 100644 --- a/test/unit/source/transfrom/cluster-spec.js +++ b/test/unit/source/transfrom/cluster-spec.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import { pointData } from '../../../asset/data/point'; import { cluster } from '../../../../src/source/transform/cluster'; -describe('hexagon Test', function() { +describe('cluster Test', function() { it('pointToCuster', function() { const dataArray = pointData.map(item => { @@ -17,7 +17,7 @@ describe('hexagon Test', function() { dataArray, extent: [ -180, -85, 180, 85 ] }; - const grid = cluster(data, { radius: 40, field: 'v', zoom: 13 }); - expect(grid.dataArray.length).eql(26); + const grid = cluster(data, { radius: 40, field: 'v', zoom: 13, bbox: [ -180, -85, 180, 85 ] }); + expect(grid.data.dataArray.length).eql(26); }); });