diff --git a/demos/01_animatePoint.html b/demos/01_animatePoint.html index 96f2acf9c4..e1ee4ae1f6 100644 --- a/demos/01_animatePoint.html +++ b/demos/01_animatePoint.html @@ -51,6 +51,7 @@ const scene = new L7.Scene({ pitch: 0, zoom: 2, maxZoom:20, + hash:true, minZoom:0, }); window.scene = scene; diff --git a/demos/02_animateline.html b/demos/02_animateline.html index 9d78576db4..55be2e09d0 100644 --- a/demos/02_animateline.html +++ b/demos/02_animateline.html @@ -88,7 +88,6 @@ scene.on('loaded', () => { // then update the map linelayer.setData(geojson); } - console.log( geojson.features[0].geometry.coordinates.length) // Request the next frame of the animation. animation = requestAnimationFrame(animateLine); } diff --git a/demos/assets/screenshots/01_animatePoint.png b/demos/assets/screenshots/01_animatePoint.png new file mode 100644 index 0000000000..2486fbebc5 Binary files /dev/null and b/demos/assets/screenshots/01_animatePoint.png differ diff --git a/demos/assets/screenshots/01_point_circle.png b/demos/assets/screenshots/01_point_circle.png index c049a40aad..829bcb7954 100644 Binary files a/demos/assets/screenshots/01_point_circle.png and b/demos/assets/screenshots/01_point_circle.png differ diff --git a/demos/assets/screenshots/01_point_column.png b/demos/assets/screenshots/01_point_column.png index 0d451be5b9..d643b3f43d 100644 Binary files a/demos/assets/screenshots/01_point_column.png and b/demos/assets/screenshots/01_point_column.png differ diff --git a/demos/assets/screenshots/01_point_distribute.png b/demos/assets/screenshots/01_point_distribute.png index f2345f8351..6ab860f52b 100644 Binary files a/demos/assets/screenshots/01_point_distribute.png and b/demos/assets/screenshots/01_point_distribute.png differ diff --git a/demos/assets/screenshots/01_point_image.png b/demos/assets/screenshots/01_point_image.png index d22999aecd..974b17a2c4 100644 Binary files a/demos/assets/screenshots/01_point_image.png and b/demos/assets/screenshots/01_point_image.png differ diff --git a/demos/assets/screenshots/02_animateline.png b/demos/assets/screenshots/02_animateline.png new file mode 100644 index 0000000000..dcce8a146f Binary files /dev/null and b/demos/assets/screenshots/02_animateline.png differ diff --git a/demos/assets/screenshots/02_contour.png b/demos/assets/screenshots/02_contour.png index 8832d56b21..014d9d3b15 100644 Binary files a/demos/assets/screenshots/02_contour.png and b/demos/assets/screenshots/02_contour.png differ diff --git a/demos/assets/screenshots/02_oneBletoneRoad.png b/demos/assets/screenshots/02_oneBletoneRoad.png index 9f9d920822..1efcc4c127 100644 Binary files a/demos/assets/screenshots/02_oneBletoneRoad.png and b/demos/assets/screenshots/02_oneBletoneRoad.png differ diff --git a/demos/assets/screenshots/03_1_extrude_polygon.png b/demos/assets/screenshots/03_1_extrude_polygon.png index c049a40aad..3bfd17c60b 100644 Binary files a/demos/assets/screenshots/03_1_extrude_polygon.png and b/demos/assets/screenshots/03_1_extrude_polygon.png differ diff --git a/demos/assets/screenshots/03_choropleths_polygon.png b/demos/assets/screenshots/03_choropleths_polygon.png index 64e3e8d621..fb36580103 100644 Binary files a/demos/assets/screenshots/03_choropleths_polygon.png and b/demos/assets/screenshots/03_choropleths_polygon.png differ diff --git a/demos/assets/screenshots/04_image.png b/demos/assets/screenshots/04_image.png index c049a40aad..a9dabd3b55 100644 Binary files a/demos/assets/screenshots/04_image.png and b/demos/assets/screenshots/04_image.png differ diff --git a/demos/assets/screenshots/05_raster_dem.png b/demos/assets/screenshots/05_raster_dem.png index c049a40aad..829bcb7954 100644 Binary files a/demos/assets/screenshots/05_raster_dem.png and b/demos/assets/screenshots/05_raster_dem.png differ diff --git a/demos/assets/screenshots/06_text.png b/demos/assets/screenshots/06_text.png index c049a40aad..e4a6de2833 100644 Binary files a/demos/assets/screenshots/06_text.png and b/demos/assets/screenshots/06_text.png differ diff --git a/demos/assets/screenshots/07_city.png b/demos/assets/screenshots/07_city.png index c049a40aad..023f8e252b 100644 Binary files a/demos/assets/screenshots/07_city.png and b/demos/assets/screenshots/07_city.png differ diff --git a/demos/assets/screenshots/08_point_shape.png b/demos/assets/screenshots/08_point_shape.png index 68525450f4..24873ac275 100644 Binary files a/demos/assets/screenshots/08_point_shape.png and b/demos/assets/screenshots/08_point_shape.png differ diff --git a/demos/assets/screenshots/grid.png b/demos/assets/screenshots/grid.png new file mode 100644 index 0000000000..6ab860f52b Binary files /dev/null and b/demos/assets/screenshots/grid.png differ diff --git a/demos/assets/screenshots/heatmap.png b/demos/assets/screenshots/heatmap.png new file mode 100644 index 0000000000..619506ae98 Binary files /dev/null and b/demos/assets/screenshots/heatmap.png differ diff --git a/demos/assets/screenshots/hexgon.png b/demos/assets/screenshots/hexgon.png new file mode 100644 index 0000000000..6ab860f52b Binary files /dev/null and b/demos/assets/screenshots/hexgon.png differ diff --git a/demos/assets/screenshots/index.png b/demos/assets/screenshots/index.png new file mode 100644 index 0000000000..939021031a Binary files /dev/null and b/demos/assets/screenshots/index.png differ diff --git a/src/core/atlas/font-manager.js b/src/core/atlas/font-manager.js index 0fdb8bb53f..b51ffe17e3 100644 --- a/src/core/atlas/font-manager.js +++ b/src/core/atlas/font-manager.js @@ -1,7 +1,7 @@ import TinySDF from '@mapbox/tiny-sdf'; -import { buildMapping } from '../../../../util/font-util'; -import * as THREE from '../../../../core/three'; -import LRUCache from './lru-cache'; +import { buildMapping } from '../../util/font-util'; +import * as THREE from '../../core/three'; +import LRUCache from '../../util/lru-cache'; export const DEFAULT_CHAR_SET = getDefaultCharacterSet(); export const DEFAULT_FONT_FAMILY = 'sans-serif'; export const DEFAULT_FONT_WEIGHT = 'normal'; diff --git a/src/core/atlas/icon-manager.js b/src/core/atlas/icon-manager.js index e69de29bb2..74d7b9653f 100644 --- a/src/core/atlas/icon-manager.js +++ b/src/core/atlas/icon-manager.js @@ -0,0 +1,52 @@ +import { buildIconMaping } from '../../util/font-util'; +import * as THREE from '../../../../core/three'; +const BUFFER = 3; +const MAX_CANVAS_WIDTH = 1024; +export default class IconManager { + constructor() { + this._getIcon = null; + this._mapping = {}; + this._autoPacking = false; + this.iconData = {}; + this._canvas = document.createElement('canvas'); + this._texture = new THREE.Texture(this._canvas); + this.ctx = this._canvas.getContext('2d'); + } + getTexture() { + return this._texture; + } + + _updateIconAtlas() { + this._canvas.width = MAX_CANVAS_WIDTH; + this._canvas.height = this._canvasHeigth; + for (const key in this.mapping) { + const icon = this.mapping[key]; + const { x, y, image } = icon; + this.ctx.drawImage(image, x, y, this.imageWidth, this.imageWidth); + } + this.texture.magFilter = THREE.LinearFilter; + this.texture.minFilter = THREE.LinearFilter; + this.texture.needsUpdate = true; + } + + addImage(id, opt) { + this._loadImage(opt).then(image => { + this.iconData.push({ id, image }); + const { mapping, canvasHeight } = buildIconMaping(this.iconData, BUFFER, MAX_CANVAS_WIDTH); + this._mapping = mapping; + this._canvasHeigth = canvasHeight; + }); + } + _loadImage(url) { + return new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => { + resolve(image); + }; + image.onerror = function() { + reject(new Error('Could not load image at ' + url)); + }; + image.src = url; + }); + } +} diff --git a/src/core/atlas/lru-cache.js b/src/core/atlas/lru-cache.js deleted file mode 100644 index 8b417e53e2..0000000000 --- a/src/core/atlas/lru-cache.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * 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 290e64a6fc..507729b617 100644 --- a/src/core/layer.js +++ b/src/core/layer.js @@ -10,6 +10,7 @@ import source from './source'; import pickingFragmentShader from '../core/engine/picking/picking_frag.glsl'; import { getInteraction } from '../interaction/index'; import Attr from '../attr/index'; +import diff from '../util/diff'; import Util from '../util'; import Global from '../global'; let id = 1; @@ -36,6 +37,7 @@ export default class Layer extends Base { attrOptions: { }, scaleOptions: {}, + preScaleOptions: null, scales: {}, attrs: {}, // 样式配置项 @@ -84,10 +86,11 @@ export default class Layer extends Base { * @param {*} type mesh类型是区别是填充还是边线 */ add(object, type = 'fill') { - // composer合图层绘制 + // composer合图层绘制 if (object.type === 'composer') { this._object3D = object; this.scene._engine.composerLayers.push(object); + setTimeout(() => this.scene._engine.update(), 500); return; } type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object; @@ -102,16 +105,11 @@ export default class Layer extends Base { object.onAfterRender = () => { // 每次渲染后改变状态 this.afterRender(); }; - // 更新 - if (this._needUpdateFilter) { // 动态更新数据过滤 - this._updateFilter(object); - } this._object3D.add(object); if (type === 'fill') { this._addPickMesh(object);// 不对边界线进行拾取 } - this.scene._engine.update(); - setTimeout(() => this.scene._engine.update(), 200); + setTimeout(() => this.scene._engine.update(), 500); } remove(object) { if (object.type === 'composer') { @@ -147,7 +145,6 @@ export default class Layer extends Base { return this; } color(field, values) { - this._needUpdateColor = true;// 标识颜色是否需要更新 this._createAttrOption('color', field, values, Global.colors); return this; } @@ -227,7 +224,6 @@ export default class Layer extends Base { } filter(field, values) { - this._needUpdateFilter = true; this._createAttrOption('filter', field, values, true); return this; } @@ -323,6 +319,11 @@ export default class Layer extends Base { } return scale; } + render() { + this.init(); + this.scene._engine.update(); + return this; + } // 重绘 度量, 映射,顶点构建 repaint() { this.set('scales', {}); @@ -335,11 +336,7 @@ export default class Layer extends Base { init() { this._initControllers(); this._initAttrs(); - this._scaleByZoom(); - this._initInteraction(); - this._initMapEvent(); - - this._mapping(); + this._updateDraw(); } _initInteraction() { if (this.get('allowActive')) { @@ -388,6 +385,7 @@ export default class Layer extends Base { _initAttrs() { + // 对比 options变化判断如何更新 const attrOptions = this.get('attrOptions'); for (const type in attrOptions) { if (attrOptions.hasOwnProperty(type)) { @@ -395,6 +393,55 @@ export default class Layer extends Base { } } } + _setPreOption() { + const nextAttrs = this.get('attrOptions'); + const nextStyle = this.get('styleOptions'); + this.set('preAttrOptions', Util.clone(nextAttrs)); + this.set('preStyleOption', Util.clone(nextStyle)); + } + _updateDraw() { + const preAttrs = this.get('preAttrOptions'); + const nextAttrs = this.get('attrOptions'); + const preStyle = this.get('preStyleOption'); + const nextStyle = this.get('styleOptions'); + if (preAttrs === undefined && preStyle === undefined) { + this._mapping(); + this._setPreOption(); + this._scaleByZoom(); + this._initInteraction(); + this._initMapEvent(); + this.draw(); + return; + } + if (!Util.isEqual(preAttrs.color, nextAttrs.color)) { + this._updateAttributes(this.layerMesh); + } + // 更新数据过滤 filter + if (!Util.isEqual(preAttrs.filter, nextAttrs.filter)) { + // 更新color; + this._updateAttributes(this.layerMesh); + } + // 更新Size + if (!Util.isEqual(preAttrs.size, nextAttrs.size)) { + // 更新color; + this._updateSize(); + } + // 更新形状 + if (!Util.isEqual(preAttrs.shape, nextAttrs.shape)) { + // 更新color; + this._updateShape(); + } + if (!Util.isEqual(preStyle, nextStyle)) { + // 判断新增,修改,删除 + const newStyle = {}; + Util.each(diff(preStyle, nextStyle), ({ type, key, value }) => { + (type !== 'remove') && (newStyle[key] = value); + // newStyle[key] = type === 'remove' ? null : value; + }); + this._updateStyle(newStyle); + } + this._setPreOption(); + } _updateAttr(type) { const self = this; @@ -435,7 +482,14 @@ export default class Layer extends Base { } this.emit('sizeUpdated', this.zoomSizeCache[zoom]); } + _updateStyle(option) { + const newOption = { }; + for (const key in option) { + newOption['u_' + key] = option[key]; + } + this.layerMesh.material.updateUninform(newOption); + } _mapping() { const self = this; const attrs = self.get('attrs'); @@ -445,13 +499,12 @@ export default class Layer extends Base { for (let i = 0; i < data.length; i++) { const record = data[i]; const newRecord = {}; + newRecord.id = data[i]._id; for (const k in attrs) { if (attrs.hasOwnProperty(k)) { const attr = attrs[k]; - attr.needUpdate = false; const names = attr.names; - const values = self._getAttrValues(attr, record); if (names.length > 1) { // position 之类的生成多个字段的属性 for (let j = 0; j < values.length; j++) { @@ -468,6 +521,12 @@ export default class Layer extends Base { newRecord.coordinates = record.coordinates; mappedData.push(newRecord); } + // 通过透明度过滤数据 + if (attrs.hasOwnProperty('filter')) { + mappedData.forEach(item => { + item.filter === false && (item.color[3] = 0); + }); + } this.layerData = mappedData; } @@ -580,7 +639,7 @@ export default class Layer extends Base { * 用于过滤数据 * @param {*} object 更新颜色和数据过滤 */ - _updateFilter(object) { + _updateAttributes(object) { this._updateMaping(); const filterData = this.layerData; this._activeIds = null; // 清空选中元素 @@ -621,7 +680,7 @@ export default class Layer extends Base { } else if (this.type === 'polyline') { offset = 2; } - this._object3D.position.z = offset * Math.pow(2, 20 - zoom); + this._object3D.position && (this._object3D.position.z = offset * Math.pow(2, 20 - zoom)); if (zoom < minZoom || zoom > maxZoom) { this._object3D.visible = false; } else if (this.get('visible')) { diff --git a/src/core/scene.js b/src/core/scene.js index e662d45b9f..1309cfd525 100644 --- a/src/core/scene.js +++ b/src/core/scene.js @@ -7,6 +7,7 @@ import FontAtlasManager from '../geom/buffer/point/text/font-manager'; // import { MapProvider } from '../map/AMap'; import { getMap } from '../map/index'; import Global from '../global'; +import { getInteraction } from '../interaction/index'; import { compileBuiltinModules } from '../geom/shader'; export default class Scene extends Base { getDefaultCfg() { @@ -46,6 +47,12 @@ export default class Scene extends Base { Map.asyncCamera(this._engine); this.initLayer(); this._registEvents(); + const hash = this.get('hash'); + if (hash) { + const Ctor = getInteraction('hash'); + const interaction = new Ctor({ layer: this }); + interaction._onHashChange(); + } this.emit('loaded'); }); diff --git a/src/geom/material/heatmapMateial.js b/src/geom/material/heatmapMateial.js index 315d19c7b9..d1ff1c26e0 100644 --- a/src/geom/material/heatmapMateial.js +++ b/src/geom/material/heatmapMateial.js @@ -1,35 +1,58 @@ import * as THREE from '../../core/three'; import Material from './material'; import { getModule } from '../../util/shaderModule'; +export class HeatmapColorizeMaterial extends Material { + getDefaultParameters() { + return { + uniforms: { + u_intensity: { value: 1.0 }, + u_texture: { value: null }, + u_rampColors: { value: 0 }, + u_opacity: { value: 1 } + }, + defines: { -export function HeatmapIntensityMaterial(opt) { - const { vs, fs } = getModule('heatmap_intensity'); - const material = new Material({ - uniforms: { - u_intensity: { value: opt.intensity }, - u_radius: { value: opt.radius }, - u_zoom: { value: opt.zoom } - }, - vertexShader: vs, - fragmentShader: fs, - transparent: true, - depthTest: false, - blending: THREE.AdditiveBlending - }); - return material; + } + }; + } + constructor(_uniforms, _defines = {}, parameters) { + super(parameters); + const { uniforms, defines } = this.getDefaultParameters(); + const { vs, fs } = getModule('heatmap_color'); + this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms)); + this.type = 'HeatmapColorizeMaterial'; + this.defines = Object.assign(defines, _defines); + this.vertexShader = vs; + this.fragmentShader = fs; + this.transparent = true; + } } -export function HeatmapColorizeMaterial(opt) { - const { vs, fs } = getModule('heatmap_color'); - const material = new Material({ - uniforms: { - u_texture: { value: opt.texture }, - u_colorRamp: { value: opt.colorRamp }, - u_opacity: { value: opt.opacity } - }, - vertexShader: vs, - fragmentShader: fs, - transparent: true - }); - return material; +export class HeatmapIntensityMaterial extends Material { + getDefaultParameters() { + return { + uniforms: { + u_intensity: { value: 10.0 }, + u_zoom: { value: 4 }, + u_radius: { value: 10 } + }, + defines: { + + } + }; + } + constructor(_uniforms, _defines = {}, parameters) { + super(parameters); + const { uniforms, defines } = this.getDefaultParameters(); + const { vs, fs } = getModule('heatmap_intensity'); + this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms)); + this.type = 'heatmap_intensity'; + this.defines = Object.assign(defines, _defines); + this.vertexShader = vs; + this.blending = THREE.AdditiveBlending; + this.fragmentShader = fs; + this.depthTest = false; + this.transparent = true; + } } + diff --git a/src/geom/material/material.js b/src/geom/material/material.js index 33e9d25fd0..d6b52304ce 100644 --- a/src/geom/material/material.js +++ b/src/geom/material/material.js @@ -19,7 +19,7 @@ export default class Material extends THREE.ShaderMaterial { } return uniforms; } - upDateUninform(option) { + updateUninform(option) { for (const key in option) { if (key.substr(0, 2) === 'u_') { this.setUniformsValue(key, option[key]); diff --git a/src/geom/shader/heatmap_colorize_frag.glsl b/src/geom/shader/heatmap_colorize_frag.glsl index 4cd75f67ed..4d5af730bc 100644 --- a/src/geom/shader/heatmap_colorize_frag.glsl +++ b/src/geom/shader/heatmap_colorize_frag.glsl @@ -1,11 +1,11 @@ uniform sampler2D u_texture; -uniform sampler2D u_colorRamp; +uniform sampler2D u_rampColors; uniform float u_opacity; varying vec2 v_uv; void main(){ float intensity = texture2D(u_texture,v_uv).r; - vec4 color = texture2D(u_colorRamp,vec2(0.5,1.0-intensity)); + vec4 color = texture2D(u_rampColors,vec2(0.5,1.0-intensity)); gl_FragColor = color; gl_FragColor.a = color.a * smoothstep(0.,0.05,intensity) * u_opacity; diff --git a/src/global.js b/src/global.js index 83d6ab5fc5..8bf3153379 100644 --- a/src/global.js +++ b/src/global.js @@ -3,6 +3,7 @@ * @author dxq613 */ // const Global = {}; +const FONT_FAMILY = '"-apple-system", BlinkMacSystemFont, "Segoe UI", Roboto,"Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",SimSun, "sans-serif"'; const Global = { version: '1.0.0', scene: { @@ -11,16 +12,13 @@ const Global = { center: [ 107.622, 39.266 ], minZoom: 0, maxZoom: 22, - pitch: 0 + pitch: 0, + hash: false }, - trackable: true, animate: true, - snapArray: [ 0, 1, 2, 4, 5, 10 ], height: 0, activeColor: '#2f54eb', colors: [ 'rgb(103,0,31)', 'rgb(178,24,43)', 'rgb(214,96,77)', 'rgb(244,165,130)', 'rgb(253,219,199)', 'rgb(247,247,247)', 'rgb(209,229,240)', 'rgb(146,197,222)', 'rgb(67,147,195)', 'rgb(33,102,172)', 'rgb(5,48,97)' ], - // 指定固定 tick 数的逼近值 - snapCountArray: [ 0, 1, 1.2, 1.5, 1.6, 2, 2.2, 2.4, 2.5, 3, 4, 5, 6, 7.5, 8, 10 ], size: 10000, shape: 'circle', pointShape: { @@ -28,7 +26,13 @@ const Global = { '3d': [ 'cylinder', 'triangleColumn', 'hexagonColumn', 'squareColumn' ] }, sdfHomeUrl: 'https://sdf.amap.com', - scales: { + scales: { }, + textStyle: { + fontSize: 12, + fill: '#ccc', + textBaseline: 'middle', + fontFamily: FONT_FAMILY, + textAlign: 'center' } }; diff --git a/src/interaction/factory.js b/src/interaction/factory.js index 4442e4b7e9..46143b74d0 100644 --- a/src/interaction/factory.js +++ b/src/interaction/factory.js @@ -5,10 +5,10 @@ export const getInteraction = type => { }; export const registerInteraction = (type, ctor) => { - // 注册的时候,需要校验 type 重名,不区分大小写 + // 注册的时候,需要校验 type 重名,不区分大小写 if (getInteraction(type)) { throw new Error(`Interaction type '${type}' existed.`); } - // 存储到 map 中 + // 存储到 map 中 INTERACTION_MAP[type] = ctor; }; diff --git a/src/interaction/hash.js b/src/interaction/hash.js new file mode 100644 index 0000000000..b06188a6f7 --- /dev/null +++ b/src/interaction/hash.js @@ -0,0 +1,58 @@ +import Interaction from './base'; +import throttle from '@antv/util/src/throttle.js'; +export default class Hash extends Interaction { + constructor(cfg) { + super({ + endEvent: 'camerachange', + ...cfg + }); + window.addEventListener('hashchange', this._onHashChange.bind(this), false); + this._updateHash = throttle(this._updateHashUnthrottled.bind(this), 20 * 1000 / 100); + } + end() { + this._updateHash(); + } + reset() { + this.layer._resetStyle(); + } + _getHashString() { + const center = this.layer.getCenter(), + zoom = Math.round(this.layer.getZoom() * 100) / 100, + // derived from equation: 512px * 2^z / 360 / 10^d < 0.5px + precision = Math.ceil((zoom * Math.LN2 + Math.log(512 / 360 / 0.5)) / Math.LN10), + m = Math.pow(10, precision), + lng = Math.round(center.lng * m) / m, + lat = Math.round(center.lat * m) / m, + bearing = this.layer.getRotation(), + pitch = this.layer.getPitch(); + let hash = ''; + hash += `#${zoom}/${lat}/${lng}`; + if (bearing || pitch) hash += (`/${Math.round(bearing * 10) / 10}`); + if (pitch) hash += (`/${Math.round(pitch)}`); + return hash; + } + _onHashChange() { + const loc = window.location.hash.replace('#', '').split('/'); + if (loc.length >= 3) { + this.layer.setStatus({ + center: [ +loc[2], +loc[1] ], + zoom: +loc[0], + bearing: +(loc[3] || 0), + pitch: +(loc[4] || 0) + }); + return true; + } + return false; + } + _updateHashUnthrottled() { + const hash = this._getHashString(); + window.history.replaceState(window.history.state, '', hash); + } + destory() { + window.removeEventListener('hashchange', this._onHashChange, false); + this.layer.off('camerachange', this._updateHash); + clearTimeout(this._updateHash()); + + return this; + } +} diff --git a/src/interaction/index.js b/src/interaction/index.js index 838f27ddac..54beb99308 100644 --- a/src/interaction/index.js +++ b/src/interaction/index.js @@ -1,9 +1,11 @@ import Interaction from './base'; import Active from './active'; import Select from './select'; +import Hash from './hash'; import { getInteraction, registerInteraction } from './factory'; registerInteraction('active', Active); registerInteraction('select', Select); +registerInteraction('hash', Hash); export { Interaction, registerInteraction, getInteraction }; diff --git a/src/layer/heatmapLayer.js b/src/layer/heatmapLayer.js index 1c729001df..e47b5656fd 100644 --- a/src/layer/heatmapLayer.js +++ b/src/layer/heatmapLayer.js @@ -10,12 +10,7 @@ export default class HeatMapLayer extends Layer { this.shapeType = type; return this; } - render() { - this.draw(); - return this; - } draw() { - this.init(); this.type = 'heatmap'; switch (this.shapeType) { case 'grid' : diff --git a/src/layer/imageLayer.js b/src/layer/imageLayer.js index 92e9a60b59..95f4d49767 100644 --- a/src/layer/imageLayer.js +++ b/src/layer/imageLayer.js @@ -4,8 +4,7 @@ import ImageBuffer from '../geom/buffer/image'; // import ImageGeometry from '../geom/bufferGeometry/image'; import ImageMaterial from '../geom/material/imageMaterial'; export default class imageLayer extends Layer { - render() { - this.init(); + draw() { this.type = 'image'; const source = this.layerSource; const { opacity } = this.get('styleOptions'); diff --git a/src/layer/lineLayer.js b/src/layer/lineLayer.js index dc26bd307b..4a2046686e 100644 --- a/src/layer/lineLayer.js +++ b/src/layer/lineLayer.js @@ -7,12 +7,6 @@ export default class LineLayer extends Layer { this.shapeType = type; return this; } - render() { - this.type = 'polyline'; - this.init(); - this.draw(); - return this; - } preRender() { if ( this.animateDuration > 0 && @@ -25,6 +19,7 @@ export default class LineLayer extends Layer { } } draw() { + this.type = 'polyline'; const layerData = this.layerData; const style = this.get('styleOptions'); const animateOptions = this.get('animateOptions'); diff --git a/src/layer/pointLayer.js b/src/layer/pointLayer.js index 8168a67c56..1e6efc7ae3 100644 --- a/src/layer/pointLayer.js +++ b/src/layer/pointLayer.js @@ -14,21 +14,8 @@ const { pointShape } = Global; */ export default class PointLayer extends Layer { - render() { - this.type = 'point'; - this.init(); - if (!this._hasRender) { - this.draw(); - this._hasRender = true; - } else { - this._initAttrs(); - this._needUpdateFilter || this._needUpdateColor - ? this._updateFilter() - : null; - } - return this; - } draw() { + this.type = 'point'; const { stroke, fill } = this.get('styleOptions'); const style = this.get('styleOptions'); const activeOption = this.get('activedOptions'); diff --git a/src/layer/polygonLayer.js b/src/layer/polygonLayer.js index 267e29cc9b..e25c052038 100644 --- a/src/layer/polygonLayer.js +++ b/src/layer/polygonLayer.js @@ -6,18 +6,6 @@ export default class PolygonLayer extends Layer { this.shape = type; return this; } - render() { - if (!this._hasRender) { // 首次渲染 - this._hasRender = true; - this.draw(); - } else { - - this._initAttrs(); - (this._needUpdateFilter || this._needUpdateColor) ? this._updateFilter(this.layerMesh) : null; - // TODO update Style; - } - return this; - } draw() { this.init(); this.type = 'polygon'; diff --git a/src/layer/rasterLayer.js b/src/layer/rasterLayer.js index f3816da695..40075e54c0 100644 --- a/src/layer/rasterLayer.js +++ b/src/layer/rasterLayer.js @@ -5,9 +5,8 @@ import { RasterBuffer } from '../geom/buffer/raster'; export default class RasterLayer extends Layer { - render() { + draw() { this.type = 'raster'; - this.init(); const source = this.layerSource; // 加载 完成事件 const styleOptions = this.get('styleOptions'); diff --git a/src/layer/render/heatmap/heatmap.js b/src/layer/render/heatmap/heatmap.js index b035f38942..c03d7f9b46 100644 --- a/src/layer/render/heatmap/heatmap.js +++ b/src/layer/render/heatmap/heatmap.js @@ -10,7 +10,8 @@ import * as THREE from '../../../core/three'; export function drawHeatmap(layer) { const colors = layer.get('styleOptions').rampColors; - layer.colorRamp = createColorRamp(colors); + + layer.rampColors = createColorRamp(colors); const heatmap = new heatmapPass(layer); const copy = new copyPass(layer); copy.renderToScreen = true; @@ -18,31 +19,42 @@ export function drawHeatmap(layer) { composer.addPass(heatmap); composer.addPass(copy); layer.add(composer); + layer.scene._engine.update(); + layer._updateStyle = style => { + if (style.rampColors) { + style.rampColors = createColorRamp(style.rampColors); + } + const newOption = { }; + for (const key in style) { + newOption['u_' + key] = style[key]; + } + heatmap.scene.children[0].material.updateUninform(newOption); + copy.scene.children[0].material.updateUninform(newOption); + }; } - function heatmapPass(layer) { const scene = new THREE.Scene(); const style = layer.get('styleOptions'); const data = layer.layerData; const camera = layer.scene._engine._camera; - // get attributes data + // get attributes data const buffer = new HeatmapBuffer({ data }); const attributes = buffer.attributes; - // create geometery + // create geometery const geometry = new THREE.BufferGeometry(); - // geometry.setIndex(attributes.indices); + // geometry.setIndex(attributes.indices); geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3)); geometry.addAttribute('a_dir', new THREE.Float32BufferAttribute(attributes.dirs, 2)); geometry.addAttribute('a_weight', new THREE.Float32BufferAttribute(attributes.weights, 1)); const material = new HeatmapIntensityMaterial({ - intensity: style.intensity, - radius: style.radius, - zoom: layer.scene.getZoom() - }); + u_intensity: style.intensity, + u_radius: style.radius, + u_zoom: layer.scene.getZoom() + }, {}); const mesh = new THREE.Mesh(geometry, material); scene.add(mesh); scene.onBeforeRender = () => { // 每次渲染前改变状态 @@ -55,9 +67,9 @@ function heatmapPass(layer) { function copyPass(layer) { const style = layer.get('styleOptions'); const material = new HeatmapColorizeMaterial({ - colorRamp: layer.colorRamp, - opacity: style.opacity - }); + u_rampColors: layer.rampColors, + u_opacity: style.opacity + }, {}); const copyPass = new ShaderPass(material, 'u_texture'); return copyPass; } diff --git a/src/layer/render/line/drawMeshLine.js b/src/layer/render/line/drawMeshLine.js index bb3fbe15c7..37eaf2ad25 100644 --- a/src/layer/render/line/drawMeshLine.js +++ b/src/layer/render/line/drawMeshLine.js @@ -33,7 +33,7 @@ export default function DrawLine(attributes, cfg, layer) { } = animateOptions; layer.animateDuration = layer.scene._engine.clock.getElapsedTime() + duration * repeat; - lineMaterial.upDateUninform({ + lineMaterial.updateUninform({ u_duration: duration, u_interval: interval, u_trailLength: trailLength diff --git a/src/layer/render/polygon/drawAnimate.js b/src/layer/render/polygon/drawAnimate.js index e8a4883bb5..1e7bb408fa 100644 --- a/src/layer/render/polygon/drawAnimate.js +++ b/src/layer/render/polygon/drawAnimate.js @@ -25,7 +25,7 @@ export default function DrawAnimate(attributes, style) { } DrawAnimate.prototype.updateStyle = function(style) { - this.fillPolygonMesh.material.upDateUninform({ + this.fillPolygonMesh.material.updateUninform({ u_opacity: style.opacity, u_baseColor: style.baseColor, u_brightColor: style.brightColor, diff --git a/src/map/AMap.js b/src/map/AMap.js index b5666bee4a..c4ba78e2a5 100644 --- a/src/map/AMap.js +++ b/src/map/AMap.js @@ -141,6 +141,9 @@ export default class GaodeMap extends Base { scene.setRotation = rotation => { return map.setRotation(rotation); }; + scene.setStatus = status => { + return map.setStatus(status); + }; scene.zoomIn = () => { return map.zoomIn(); }; diff --git a/src/util/diff.js b/src/util/diff.js new file mode 100644 index 0000000000..949b8fd08d --- /dev/null +++ b/src/util/diff.js @@ -0,0 +1,38 @@ +import * as _ from '@antv/util'; +export default function diff(a, b) { + // Throw is a or b are not objects. + if (!_.isPlainObject(a)) { + throw new Error('First parameter to diff() is not an object'); + } + if (!_.isPlainObject(b)) { + throw new Error('Second parameter to diff() is not an object'); + } + + const changes = []; + const keysA = _.keys(a); + const keysB = _.keys(b); + + // Find the items in A that are not in B. + _.each(_.difference(keysA, keysB), key => { + changes.push({ type: 'remove', key, value: a[key] }); + }); + + // Find the items in B that are not in A. + _.each(_.difference(keysB, keysA), key => { + changes.push({ type: 'add', key, value: b[key] }); + }); + + // Find the items that are in both, but have changed. + _.each(intersection(keysA, keysB), key => { + if (!_.isEqual(a[key], b[key])) { + changes.push({ type: 'update', key, value: b[key] }); + } + }); + + return changes; +} +function intersection(keysA, keysB) { + return keysA.filter(key => { + return keysB.indexOf(key) > -1; + }); +} diff --git a/src/util/font-util.js b/src/util/font-util.js index 1e0b96a98d..42c2387cb3 100644 --- a/src/util/font-util.js +++ b/src/util/font-util.js @@ -41,3 +41,73 @@ export function buildMapping({ canvasHeight: nextPowOfTwo(yOffset + (row + 1) * rowHeight) }; } + +function buildRowMapping(mapping, columns, yOffset) { + for (let i = 0; i < columns.length; i++) { + const { icon, xOffset } = columns[i]; + mapping[icon.id] = Object.assign({}, icon, { + x: xOffset, + y: yOffset, + image: icon.image + }); + } +} +export function resizeImage(ctx, imageData, width, height) { + const { naturalWidth, naturalHeight } = imageData; + if (width === naturalWidth && height === naturalHeight) { + return imageData; + } + + ctx.canvas.height = height; + ctx.canvas.width = width; + + ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); + + // image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight + ctx.drawImage(imageData, 0, 0, naturalWidth, naturalHeight, 0, 0, width, height); + + return ctx.canvas; +} + +export function buildIconMaping({ icons, buffer, maxCanvasWidth }) { + let xOffset = 0; + let yOffset = 0; + let rowHeight = 0; + let columns = []; + const mapping = {}; + for (let i = 0; i < icons.length; i++) { + const icon = icons[i]; + if (!mapping[icon.id]) { + const { height, width } = icon; + + // fill one row + if (xOffset + width + buffer > maxCanvasWidth) { + buildRowMapping(mapping, columns, yOffset); + + xOffset = 0; + yOffset = rowHeight + yOffset + buffer; + rowHeight = 0; + columns = []; + } + + columns.push({ + icon, + xOffset + }); + + xOffset = xOffset + width + buffer; + rowHeight = Math.max(rowHeight, height); + } + } + + if (columns.length > 0) { + buildRowMapping(mapping, columns, yOffset); + } + + const canvasHeight = nextPowOfTwo(rowHeight + yOffset + buffer); + + return { + mapping, + canvasHeight + }; +} diff --git a/src/util/index.js b/src/util/index.js deleted file mode 100644 index 5d3404b1b8..0000000000 --- a/src/util/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import CommonUtil from './common'; -import DomUtil from './dom'; - -const Util = {}; - -CommonUtil.merge(Util, CommonUtil, DomUtil, { - mixin(c, mixins) { - const Param = c.CFG ? 'CFG' : 'ATTRS'; - if (c && mixins) { - c._mixins = mixins; - c[Param] = c[Param] || {}; - const temp = {}; - Util.each(mixins, function(mixin) { - Util.augment(c, mixin); - const attrs = mixin[Param]; - if (attrs) { - Util.merge(temp, attrs); - } - }); - c[Param] = Util.merge(temp, c[Param]); - } - } -}); - -export default Util; diff --git a/test/unit/shader-module/base-spec.js b/test/unit/shader-module/base-spec.js index 36410577d1..55ae9c0cba 100644 --- a/test/unit/shader-module/base-spec.js +++ b/test/unit/shader-module/base-spec.js @@ -22,8 +22,8 @@ describe('test shader module', function() { registerModule('common', commonModule); registerModule('module1', module1); it('should import a module correctly.', function() { - // expect(vs).eq('#define PI 3.14'); - // expect(fs.replace(/(\s+)|(\n)+|(\r\n)+/g, '')).eqls('#ifdefGL_FRAGMENT_PRECISION_HIGHprecisionhighpfloat;#elseprecisionmediumpfloat;#endif'); + // expect(vs).eq('#define PI 3.14'); + // expect(fs.replace(/(\s+)|(\n)+|(\r\n)+/g, '')).eqls('#ifdefGL_FRAGMENT_PRECISION_HIGHprecisionhighpfloat;#elseprecisionmediumpfloat;#endif'); }); });