mirror of https://gitee.com/antv-l7/antv-l7
feat(layer): add repaint method
This commit is contained in:
parent
cffef3aa2f
commit
b982c07272
|
@ -0,0 +1,105 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
<title>point_circle</title>
|
||||
<style>
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
.amap-maps {
|
||||
cursor: auto !important
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
var radius = 0.2;
|
||||
|
||||
function pointOnCircle(angle) {
|
||||
return {
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {},
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [
|
||||
120.19382669582967 + Math.cos(angle) * radius,
|
||||
30.258134 + Math.sin(angle) * radius
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
console.log(pointOnCircle(10))
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'light', // 样式URL
|
||||
center: [ 120.19382669582967, 30.258134 ],
|
||||
pitch: 0,
|
||||
zoom: 2,
|
||||
maxZoom:20,
|
||||
minZoom:0,
|
||||
});
|
||||
window.scene = scene;
|
||||
scene.on('loaded', () => {
|
||||
const circleLayer = scene.PointLayer({
|
||||
zIndex: 0,
|
||||
})
|
||||
.source(pointOnCircle(0))
|
||||
.shape('circle')
|
||||
.size(10) // default 1
|
||||
.active(true)
|
||||
.color('#2894E0')
|
||||
.style({
|
||||
stroke: 'rgb(255,255,255)',
|
||||
strokeWidth: 1,
|
||||
opacity: 0.8
|
||||
})
|
||||
.render();
|
||||
|
||||
function animateMarker(timestamp) {
|
||||
|
||||
circleLayer.setData(pointOnCircle(timestamp / 1000));
|
||||
requestAnimationFrame(animateMarker);
|
||||
}
|
||||
animateMarker(0);
|
||||
|
||||
/**
|
||||
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);
|
||||
|
||||
});
|
||||
**/
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</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);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="geometry" content="diagram">
|
||||
<link rel="stylesheet" href="./assets/common.css">
|
||||
<link rel="stylesheet" href="./assets/info.css">
|
||||
<title>animateLine</title>
|
||||
<style>
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
.amap-maps {
|
||||
cursor: auto !important
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="./assets/jquery-3.2.1.min.js"></script>
|
||||
<script src="./assets/dat.gui.min.js"></script>
|
||||
<script src="../build/L7.js"></script>
|
||||
<script>
|
||||
var geojson = {
|
||||
"type": "FeatureCollection",
|
||||
"features": [{
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[0, 0],
|
||||
[0.001, 0.0001]
|
||||
]
|
||||
}
|
||||
} ]
|
||||
};
|
||||
var speedFactor = 30; // number of frames per longitude degree
|
||||
var animation; // to store and cancel the animation
|
||||
var startTime = 0;
|
||||
var progress = 0; // progress = timestamp - startTime
|
||||
var resetTime = false; // indicator of whether time reset is needed for the animation
|
||||
var pauseButton = document.getElementById('pause');
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'light', // 样式URL
|
||||
center: [ 120.19382669582967, 30.258134 ],
|
||||
pitch: 0,
|
||||
zoom: 2,
|
||||
maxZoom:20,
|
||||
minZoom:0,
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
const linelayer = scene.LineLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(geojson)
|
||||
.size([2,1])
|
||||
.shape('line')
|
||||
.color( "#2894E0")
|
||||
.render();
|
||||
startTime = performance.now();
|
||||
animateLine(0);
|
||||
function animateLine(timestamp) {
|
||||
if (resetTime) {
|
||||
// resume previous progress
|
||||
startTime = performance.now() - progress;
|
||||
resetTime = false;
|
||||
} else {
|
||||
progress = timestamp - startTime;
|
||||
}
|
||||
|
||||
// restart if it finishes a loop
|
||||
if (progress > speedFactor * 360) {
|
||||
startTime = timestamp;
|
||||
geojson.features[0].geometry.coordinates = [
|
||||
[0, 0],
|
||||
[0.001, 0.0001]
|
||||
];
|
||||
} else {
|
||||
var x = progress / speedFactor;
|
||||
// draw a sine wave with some math.
|
||||
var y = Math.sin(x * Math.PI / 90) * 40;
|
||||
// append new coordinates to the lineString
|
||||
|
||||
geojson.features[0].geometry.coordinates.push([x, y]);
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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}`;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,14 +69,6 @@ export default class Scene extends Base {
|
|||
|
||||
super.off(type, hander);
|
||||
}
|
||||
_initAttribution() {
|
||||
const message = '<a href="http://antv.alipay.com/zh-cn/index.html title="Large-scale WebGL-powered Geospatial Data Visualization">AntV | L7 </a>';
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import Base from '../core/base';
|
||||
export class GeomBase extends Base {
|
||||
geometryBuffer() {
|
||||
|
||||
}
|
||||
geometry() {
|
||||
}
|
||||
|
||||
material() {
|
||||
|
||||
}
|
||||
|
||||
drawMesh() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default GeomBase;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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
|
||||
|
|
|
@ -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.)){
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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){
|
||||
|
|
|
@ -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),
|
||||
|
|
13
src/index.js
13
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
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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 };
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -14,4 +14,5 @@ registerLayer('RasterLayer', RasterLayer);
|
|||
registerLayer('HeatmapLayer', HeatmapLayer);
|
||||
|
||||
export { LAYER_MAP } from './factory';
|
||||
export { registerLayer };
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ],
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue