mirror of https://gitee.com/antv-l7/antv-l7
Merge branch 'grid-heatMap' into 'master'
feat(hexagon): hexagon heatmap See merge request !6
This commit is contained in:
commit
d42d04caf3
|
@ -1,5 +1,4 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
babelrc: {
|
||||
presets: [
|
||||
|
@ -10,7 +9,7 @@ module.exports = {
|
|||
include: [
|
||||
'src/**/*.js',
|
||||
'test/**/*.js',
|
||||
'node_modules/three/**/*.js',
|
||||
'node_modules/_three@0.96.0@three/**/*.js',
|
||||
'node_modules/simple-statistics/src/*.js'
|
||||
],
|
||||
exclude: [
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<!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">
|
||||
<title>point_circle</title>
|
||||
<style>
|
||||
#map { position:absolute; top:0; bottom:0; width:100%; }
|
||||
</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>
|
||||
|
||||
const scene = new L7.Scene({
|
||||
id: 'map',
|
||||
mapStyle: 'dark', // 样式URL
|
||||
center: [120.132624,30.281774],
|
||||
pitch: 0,
|
||||
zoom: 10
|
||||
});
|
||||
scene.on('loaded', () => {
|
||||
$.get('https://gw.alipayobjects.com/os/basement_prod/7359a5e9-3c5e-453f-b207-bc892fb23b84.csv', data => {
|
||||
var layer = scene.HeatMapLayer({
|
||||
zIndex: 2
|
||||
})
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'csv',
|
||||
x: 'lng',
|
||||
y: 'lat'
|
||||
},
|
||||
transforms:[
|
||||
{
|
||||
type: 'map',
|
||||
callback:function(item){
|
||||
const [x, y] = item.coordinates;
|
||||
item.lat = item.lat*1;
|
||||
item.lng = item.lng*1;
|
||||
item.v = item.v *1;
|
||||
item.coordinates = [x*1,y*1];
|
||||
return item;
|
||||
}
|
||||
|
||||
},
|
||||
{
|
||||
type: 'hexagon',
|
||||
size: 6000,
|
||||
field:'v',
|
||||
method:'sum'
|
||||
}
|
||||
]
|
||||
})
|
||||
.active(true)
|
||||
.shape('hexagon')
|
||||
.style({
|
||||
coverage: 0.9,
|
||||
angle: 0,
|
||||
})
|
||||
.color('count', ["#002466","#105CB3","#2894E0","#CFF6FF","#FFF5B8","#FFAB5C","#F27049","#730D1C"])
|
||||
.render();
|
||||
console.log(layer);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -78,11 +78,11 @@
|
|||
"prepublishOnly": "npm run build-lib && npm run dist",
|
||||
"screenshot": "node ./bin/screenshot.js",
|
||||
"start": "npm run dev",
|
||||
"test": "torch --compile --renderer --recursive test/unit",
|
||||
"test": "torch --compile-opts ./.torch.compile.opts.js --compile --renderer --recursive test/unit",
|
||||
"test-all": "npm run test && npm run test-bugs",
|
||||
"test-bugs": "torch --compile --renderer --recursive test/bugs",
|
||||
"test-bugs-live": "torch --compile --interactive --watch --recursive test/bugs",
|
||||
"test-live": "torch --compile --interactive --watch --recursive test/unit",
|
||||
"test-live": "torch --compile --interactive --watch --recursive test/unit",
|
||||
"watch": "webpack --config webpack-dev.config.js",
|
||||
"win-dev": "node ./bin/win-dev.js"
|
||||
},
|
||||
|
|
|
@ -35,4 +35,4 @@ export {
|
|||
BufferAttribute
|
||||
} from 'three/src/core/BufferAttribute.js';
|
||||
|
||||
// export * from '../../build/Three.js';
|
||||
// export * from '../../build/three.js';
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import { polygonPath } from '../../shape/path';
|
||||
import { fill } from '../../shape/polygon';
|
||||
export default function hexagonBuffer(layerData) {
|
||||
const attribute = {
|
||||
vertices: [],
|
||||
miter: [],
|
||||
colors: [],
|
||||
pickingIds: []
|
||||
};
|
||||
const a = Math.cos(Math.PI / 6);
|
||||
const points = [
|
||||
[ 0, -1, 0 ],
|
||||
[ -a, -0.5, 0 ],
|
||||
[ -a, 0.5, 0 ],
|
||||
[ 0, 1, 0 ],
|
||||
[ a, 0.5, 0 ],
|
||||
[ a, -0.5, 0 ],
|
||||
[ 0, -1, 0 ]
|
||||
];
|
||||
// const hexgonPoints = polygonPath(6);
|
||||
const hexgonFill = fill([ points ]);
|
||||
const { positionsIndex, positions } = hexgonFill;
|
||||
layerData.forEach(element => {
|
||||
positionsIndex.forEach(pointIndex => {
|
||||
attribute.vertices.push(...element.coordinates);
|
||||
attribute.miter.push(positions[pointIndex][0], positions[pointIndex][1]);
|
||||
attribute.pickingIds.push(element.id);
|
||||
attribute.colors.push(...element.color);
|
||||
});
|
||||
});
|
||||
return attribute;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import grid_frag from '../shader/hexagon_frag.glsl';
|
||||
import grid_vert from '../shader/hexagon_vert.glsl';
|
||||
import Material from './material';
|
||||
|
||||
|
||||
export default class hexagonMaterial extends Material {
|
||||
getDefaultParameters() {
|
||||
return {
|
||||
uniforms: {
|
||||
u_opacity: { value: 1.0 },
|
||||
u_time: { value: 0 },
|
||||
u_radius: { value: 0.01 },
|
||||
u_angle: { value: 0.01 },
|
||||
u_coverage: { value: 0.8 }
|
||||
},
|
||||
defines: {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
constructor(_uniforms, _defines, parameters) {
|
||||
super(parameters);
|
||||
const { uniforms, defines } = this.getDefaultParameters();
|
||||
this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms));
|
||||
this.type = 'hexagonMaterial';
|
||||
this.defines = Object.assign(defines, _defines);
|
||||
this.vertexShader = grid_vert;
|
||||
this.fragmentShader = grid_frag;
|
||||
this.transparent = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
precision highp float;
|
||||
uniform float u_opacity;
|
||||
varying vec4 v_color;
|
||||
void main() {
|
||||
vec4 color = v_color;
|
||||
gl_FragColor = color;
|
||||
gl_FragColor.a =color.a*u_opacity;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
precision highp float;
|
||||
attribute vec2 miter;
|
||||
attribute vec4 a_color;
|
||||
uniform float u_radius;
|
||||
uniform float u_coverage;
|
||||
uniform float u_angle;
|
||||
varying vec4 v_color;
|
||||
|
||||
void main() {
|
||||
mat4 matModelViewProjection = projectionMatrix * modelViewMatrix;
|
||||
mat2 rotationMatrix = mat2(cos(u_angle), sin(u_angle), -sin(u_angle), cos(u_angle));
|
||||
v_color = a_color;
|
||||
vec2 offset =vec2(rotationMatrix * miter * u_radius * u_coverage );
|
||||
float x = position.x + offset.x;
|
||||
float y = position.y + offset.y;
|
||||
gl_Position = matModelViewProjection * vec4(x, y, position.z, 1.0);
|
||||
}
|
|
@ -2,7 +2,6 @@ precision highp float;
|
|||
attribute vec4 a_color;
|
||||
attribute float a_size;
|
||||
attribute float a_shape;
|
||||
attribute vec4 a_idColor;
|
||||
uniform vec4 u_stroke;
|
||||
uniform float u_strokeWidth;
|
||||
uniform float u_opacity;
|
||||
|
|
|
@ -3,7 +3,6 @@ precision highp float;
|
|||
#define diffuseRatio 0.4
|
||||
#define specularRatio 0.1
|
||||
attribute vec4 a_color;
|
||||
attribute vec4 a_idColor;
|
||||
attribute vec2 faceUv;
|
||||
attribute vec3 a_shape;
|
||||
attribute vec3 a_size;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Layer from '../core/layer';
|
||||
import gridBuffer from '../geom/buffer/heatmap/grid';
|
||||
import DrawGrid from './render/heatmap/gird';
|
||||
import DrawHexagon from './render/heatmap/hexagon';
|
||||
import hexagonBuffer from '../geom/buffer/heatmap/hexagon';
|
||||
|
||||
export default class HeatMapLayer extends Layer {
|
||||
shape(type) {
|
||||
|
@ -13,6 +15,30 @@ export default class HeatMapLayer extends Layer {
|
|||
}
|
||||
_prepareRender() {
|
||||
this.init();
|
||||
switch (this.shapeType) {
|
||||
case 'grid' :
|
||||
this._drawGrid();
|
||||
break;
|
||||
case 'hexagon' :
|
||||
this._drawHexagon();
|
||||
break;
|
||||
default:
|
||||
this._drawGrid();
|
||||
}
|
||||
}
|
||||
_drawHexagon() {
|
||||
const style = this.get('styleOptions');
|
||||
const { radius } = this.layerSource.data;
|
||||
this._buffer = new hexagonBuffer(this.layerData);
|
||||
const config = {
|
||||
...style,
|
||||
radius
|
||||
};
|
||||
const Mesh = new DrawHexagon(this._buffer, config);
|
||||
this.add(Mesh);
|
||||
|
||||
}
|
||||
_drawGrid() {
|
||||
this.type = 'heatmap';
|
||||
const style = this.get('styleOptions');
|
||||
const { xOffset, yOffset } = this.layerSource.data;
|
||||
|
@ -25,4 +51,5 @@ export default class HeatMapLayer extends Layer {
|
|||
const girdMesh = new DrawGrid(this._buffer, config);
|
||||
this.add(girdMesh);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import * as THREE from '../../../core/three';
|
||||
import GridMaterial from '../../../geom/material/hexagon';
|
||||
export default function DrawHexagon(attributes, style) {
|
||||
const { opacity, radius, angle, coverage } = style;
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3));
|
||||
geometry.addAttribute('miter', new THREE.Float32BufferAttribute(attributes.miter, 2));
|
||||
geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4));
|
||||
geometry.addAttribute('pickingId', new THREE.Float32BufferAttribute(attributes.pickingIds, 1));
|
||||
const material = new GridMaterial({
|
||||
u_opacity: opacity,
|
||||
u_radius: radius,
|
||||
u_angle: angle / 180 * Math.PI,
|
||||
u_coverage: coverage
|
||||
}, {
|
||||
SHAPE: false
|
||||
});
|
||||
const hexgonMesh = new THREE.Mesh(geometry, material);
|
||||
return hexgonMesh;
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import raster from './parser/raster';
|
|||
|
||||
import { registerTransform, registerParser } from './factory';
|
||||
import { aggregatorToGrid } from './transform/grid';
|
||||
import { pointToHexbin } from './transform/hexagon';
|
||||
import { map } from './transform/map';
|
||||
|
||||
registerParser('geojson', geojson);
|
||||
|
@ -18,6 +19,7 @@ registerParser('raster', raster);
|
|||
// 注册transform
|
||||
|
||||
registerTransform('grid', aggregatorToGrid);
|
||||
registerTransform('hexagon', pointToHexbin);
|
||||
registerTransform('map', map);
|
||||
|
||||
export { getTransform, registerTransform, getParser, registerParser } from './factory';
|
||||
|
|
|
@ -17,8 +17,8 @@ export function aggregatorToGrid(data, option) {
|
|||
const { gridHash, gridOffset } = _pointsGridHash(dataArray, size);
|
||||
const layerData = _getGridLayerDataFromGridHash(gridHash, gridOffset, option);
|
||||
return {
|
||||
xOffset: gridOffset.xOffset / 360 * (256 << 20) / 2,
|
||||
yOffset: gridOffset.xOffset / 360 * (256 << 20) / 2,
|
||||
xOffset: gridOffset.xOffset / 360 * (256 << 20) / 2,
|
||||
dataArray: layerData
|
||||
};
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export function pointToHexbin(data, option) {
|
|||
.y(d => d.coordinates[1]);
|
||||
const hexbinBins = newHexbin(screenPoints);
|
||||
const result = {
|
||||
size: pixlSize
|
||||
radius: pixlSize
|
||||
};
|
||||
result.dataArray = hexbinBins.map((hex, index) => {
|
||||
if (option.field && option.method) {
|
||||
|
@ -31,8 +31,9 @@ export function pointToHexbin(data, option) {
|
|||
item[option.method] = hex[option.method];
|
||||
return {
|
||||
...item,
|
||||
count: hex.length,
|
||||
coordinates: unProjectFlat([ hex.x, hex.y ]),
|
||||
id: index + 1
|
||||
_id: index + 1
|
||||
};
|
||||
});
|
||||
return result;
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { expect } from 'chai';
|
||||
import hexagonBuffer from '../../../../../src/geom/buffer/heatmap/hexagon';
|
||||
describe('hexagon heatMap buffer', () => {
|
||||
const layerData = [
|
||||
{ color: [ 1, 1, 0, 1 ], coordinates: [ 120.12063099925889, 30.263947783103486, 0 ], id: 1 },
|
||||
{ color: [ 1, 0.5, 0, 1 ], coordinates: [ 120.12218696039365, 30.263947783103486, 0 ], id: 2 },
|
||||
{
|
||||
color: [ 1, 0.1, 0, 1 ],
|
||||
coordinates: [ 120.12374292152843, 30.263947783103486, 0 ],
|
||||
id: 3
|
||||
},
|
||||
{ color: [ 1, 0, 0.2, 1 ], coordinates: [ 120.1252988826632, 30.263947783103486, 0 ], id: 4 },
|
||||
{
|
||||
color: [ 0, 1, 0, 1 ],
|
||||
coordinates: [ 120.12607686323062, 30.263947783103486, 0 ],
|
||||
id: 5
|
||||
},
|
||||
{
|
||||
color: [ 1, 1, 0, 1 ],
|
||||
coordinates: [ 120.12685484379799, 30.263947783103486, 0 ],
|
||||
id: 6
|
||||
},
|
||||
{ color: [ 1, 1, 1, 1 ], coordinates: [ 120.12841080493274, 30.263947783103486, 0 ], id: 7 },
|
||||
{ color: [ 0, 1, 1, 1 ], coordinates: [ 120.13230070776972, 30.263947783103486, 0 ], id: 8 },
|
||||
{ color: [ 0, 1, 0, 1 ], coordinates: [ 120.12763282436538, 30.263947783103486, 0 ], id: 9 },
|
||||
{ color: [ 1, 1, 0, 1 ], coordinates: [ 120.12996676606754, 30.263947783103486, 0 ], id: 10 }
|
||||
];
|
||||
it('hexagon buffer', () => {
|
||||
const attribute = hexagonBuffer(layerData);
|
||||
expect(attribute.colors.length).eql(480);
|
||||
expect(attribute.miter.length).eql(240);
|
||||
expect(attribute.pickingIds.length).eql(120);
|
||||
expect(attribute.vertices.length).eql(360);
|
||||
});
|
||||
});
|
|
@ -24,9 +24,8 @@ describe('test shader module', function() {
|
|||
|
||||
it('should import a module correctly.', function() {
|
||||
const { vs, fs } = getModule('module1');
|
||||
|
||||
expect(vs).eq('#define PI 3.14');
|
||||
expect(fs).eq('');
|
||||
expect(fs.replace(/(\s+)|(\n)+|(\r\n)+/g, '')).eqls('#ifdefGL_FRAGMENT_PRECISION_HIGHprecisionhighpfloat;#elseprecisionmediumpfloat;#endif');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -18,6 +18,5 @@ describe('hexagon Test', function() {
|
|||
};
|
||||
const hexgonGrid = pointToHexbin(data, { size: 100, field: 'v', method: 'sum' });
|
||||
expect(hexgonGrid.dataArray.length).eql(567);
|
||||
console.log(hexgonGrid);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,7 +4,8 @@ const pkg = require('./package.json');
|
|||
module.exports = {
|
||||
devtool: 'cheap-source-map',
|
||||
entry: {
|
||||
l7: './src/index.js'
|
||||
l7: './src/index.js',
|
||||
three: './src/core/three.js'
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
|
|
Loading…
Reference in New Issue