feat(interaction): add hash
|
@ -51,6 +51,7 @@ const scene = new L7.Scene({
|
|||
pitch: 0,
|
||||
zoom: 2,
|
||||
maxZoom:20,
|
||||
hash:true,
|
||||
minZoom:0,
|
||||
});
|
||||
window.scene = scene;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 564 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 194 KiB |
After Width: | Height: | Size: 89 KiB |
Before Width: | Height: | Size: 528 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 73 KiB |
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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')) {
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 };
|
||||
|
|
|
@ -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' :
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
|
|