mirror of https://gitee.com/antv-l7/antv-l7
Merge branch 'master' into heatmap-fix
This commit is contained in:
commit
582c4df952
|
@ -13,14 +13,14 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=15cd8a57710d40c9b7c0e3cc120f1200&plugin=Map3D"></script>
|
||||
<script src="https://webapi.amap.com/maps?v=1.4.8&key=f28fca5384129d180ad82915156a9baf&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
|
||||
mapStyle: 'amap://styles/c9f1d10cae34f8ab05e425462c5a58d7', // 样式URL
|
||||
center: [ -155, 60 ],
|
||||
pitch: 0,
|
||||
zoom: 4.5
|
||||
|
@ -35,11 +35,12 @@ scene.on('loaded', () => {
|
|||
.source(data)
|
||||
.size('mag', [ 0, 1 ]) // weight映射通道
|
||||
.style({
|
||||
intensity: 100,
|
||||
radius: 30,
|
||||
intensity: 10,
|
||||
radius: 10,
|
||||
opacity:1,
|
||||
rampColors: {
|
||||
colors: [ 'rgba(33,102,172,0.0)', 'rgb(103,169,207)', 'rgb(209,229,240)', 'rgb(253,219,199)', 'rgb(239,138,98)', 'rgb(178,24,43,1.0)' ],
|
||||
positions: [ 0, 0.2, 0.4, 0.6, 0.8, 1.0 ]
|
||||
colors: [ '#ffda45ff', '#fde725ff', '#ffc934ff', '#ffb824ff', '#ffb824ff', '#fa8100ff' ],
|
||||
positions: [ 0, 0.2, 0.4, 0.6, 0.9, 1.0 ]
|
||||
}
|
||||
})
|
||||
.render();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@antv/l7",
|
||||
"version": "1.1.5",
|
||||
"version": "1.1.7",
|
||||
"description": "Large-scale WebGL-powered Geospatial Data Visualization",
|
||||
"main": "build/l7.js",
|
||||
"browser": "build/l7.js",
|
||||
|
@ -111,6 +111,7 @@
|
|||
"polyline-normals": "^2.0.2",
|
||||
"rbush": "^2.0.2",
|
||||
"simple-statistics": "^7.0.1",
|
||||
"supercluster": "^6.0.1",
|
||||
"three": "^0.101.1",
|
||||
"venn.js": "^0.2.20",
|
||||
"viewport-mercator-project": "^5.2.0",
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import THREE from '../three';
|
||||
import CopyShader from './CopyShader';
|
||||
import ShaderPass from './ShaderPass';
|
||||
import MaskPass, {ClearMaskPass} from './MaskPass';
|
||||
import * as THREE from '../three';
|
||||
import CopyShader from './copy-shader';
|
||||
import ShaderPass from './shader-pass';
|
||||
import MaskPass, {ClearMaskPass} from './mask-pass';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
|
@ -50,7 +50,8 @@ EffectComposer.prototype = {
|
|||
this.writeBuffer = tmp;
|
||||
|
||||
},
|
||||
|
||||
visible:true,
|
||||
type:'composer',
|
||||
addPass: function ( pass ) {
|
||||
|
||||
this.passes.push( pass );
|
||||
|
@ -71,7 +72,6 @@ EffectComposer.prototype = {
|
|||
var maskActive = false;
|
||||
|
||||
var pass, i, il = this.passes.length;
|
||||
|
||||
for ( i = 0; i < il; i ++ ) {
|
||||
|
||||
pass = this.passes[ i ];
|
||||
|
@ -147,4 +147,3 @@ EffectComposer.prototype = {
|
|||
};
|
||||
|
||||
export default EffectComposer;
|
||||
THREE.EffectComposer = EffectComposer;
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*
|
||||
* Full-screen textured quad shader
|
||||
*/
|
||||
|
||||
var CopyShader = {
|
||||
|
||||
uniforms: {
|
||||
|
||||
"tDiffuse": { type: "t", value: null },
|
||||
"opacity": { type: "f", value: 1.0 }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: [
|
||||
|
||||
"varying vec2 vUv;",
|
||||
|
||||
"void main() {",
|
||||
|
||||
"vUv = uv;",
|
||||
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
|
||||
|
||||
"}"
|
||||
|
||||
].join( "\n" ),
|
||||
|
||||
fragmentShader: [
|
||||
|
||||
"uniform float opacity;",
|
||||
|
||||
"uniform sampler2D tDiffuse;",
|
||||
|
||||
"varying vec2 vUv;",
|
||||
|
||||
"void main() {",
|
||||
|
||||
"vec4 texel = texture2D( tDiffuse, vUv );",
|
||||
"gl_FragColor = opacity * texel;",
|
||||
|
||||
"}"
|
||||
|
||||
].join( "\n" )
|
||||
|
||||
};
|
||||
|
||||
export default CopyShader;
|
|
@ -0,0 +1,21 @@
|
|||
import EffectComposer from './composer';
|
||||
|
||||
export default function(renderer, container) {
|
||||
const composer = new EffectComposer(renderer);
|
||||
|
||||
const updateSize = function() {
|
||||
// TODO: Re-enable this when perf issues can be solved
|
||||
//
|
||||
// Rendering double the resolution of the screen can be really slow
|
||||
// var pixelRatio = window.devicePixelRatio;
|
||||
const pixelRatio = 1;
|
||||
|
||||
composer.setSize(container.clientWidth * pixelRatio, container.clientHeight * pixelRatio);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', updateSize, false);
|
||||
updateSize();
|
||||
|
||||
return composer;
|
||||
}
|
||||
|
|
@ -16,14 +16,16 @@ export default class Engine extends EventEmitter {
|
|||
this._scene.add(this.world);
|
||||
this._picking = Picking(this._world, this._renderer, this._camera, this._scene);
|
||||
this.clock = new THREE.Clock();
|
||||
this.composerLayers = [];
|
||||
}
|
||||
_initPostProcessing() {
|
||||
|
||||
this.composerLayers.forEach(layer => {
|
||||
layer.visible && layer.render();
|
||||
});
|
||||
}
|
||||
update() {
|
||||
|
||||
this._renderer.render(this._scene, this._camera);
|
||||
|
||||
this._initPostProcessing();
|
||||
}
|
||||
destroy() {
|
||||
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*/
|
||||
|
||||
var MaskPass = function ( scene, camera ) {
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.enabled = true;
|
||||
this.clear = true;
|
||||
this.needsSwap = false;
|
||||
|
||||
this.inverse = false;
|
||||
|
||||
};
|
||||
|
||||
MaskPass.prototype = {
|
||||
|
||||
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
||||
|
||||
var context = renderer.context;
|
||||
|
||||
// don't update color or depth
|
||||
|
||||
context.colorMask( false, false, false, false );
|
||||
context.depthMask( false );
|
||||
|
||||
// set up stencil
|
||||
|
||||
var writeValue, clearValue;
|
||||
|
||||
if ( this.inverse ) {
|
||||
|
||||
writeValue = 0;
|
||||
clearValue = 1;
|
||||
|
||||
} else {
|
||||
|
||||
writeValue = 1;
|
||||
clearValue = 0;
|
||||
|
||||
}
|
||||
|
||||
context.enable( context.STENCIL_TEST );
|
||||
context.stencilOp( context.REPLACE, context.REPLACE, context.REPLACE );
|
||||
context.stencilFunc( context.ALWAYS, writeValue, 0xffffffff );
|
||||
context.clearStencil( clearValue );
|
||||
|
||||
// draw into the stencil buffer
|
||||
|
||||
renderer.render( this.scene, this.camera, readBuffer, this.clear );
|
||||
renderer.render( this.scene, this.camera, writeBuffer, this.clear );
|
||||
|
||||
// re-enable update of color and depth
|
||||
|
||||
context.colorMask( true, true, true, true );
|
||||
context.depthMask( true );
|
||||
|
||||
// only render where stencil is set to 1
|
||||
|
||||
context.stencilFunc( context.EQUAL, 1, 0xffffffff ); // draw if == 1
|
||||
context.stencilOp( context.KEEP, context.KEEP, context.KEEP );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
var ClearMaskPass = function () {
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
};
|
||||
|
||||
ClearMaskPass.prototype = {
|
||||
|
||||
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
||||
|
||||
var context = renderer.context;
|
||||
|
||||
context.disable( context.STENCIL_TEST );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default MaskPass;
|
||||
export {ClearMaskPass as ClearMaskPass};
|
|
@ -0,0 +1,58 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*/
|
||||
|
||||
var RenderPass = function ( scene, camera, overrideMaterial, clearColor, clearAlpha ) {
|
||||
|
||||
this.scene = scene;
|
||||
this.camera = camera;
|
||||
|
||||
this.overrideMaterial = overrideMaterial;
|
||||
|
||||
this.clearColor = clearColor;
|
||||
this.clearAlpha = ( clearAlpha !== undefined ) ? clearAlpha : 1;
|
||||
|
||||
this.oldClearColor = new THREE.Color();
|
||||
this.oldClearAlpha = 1;
|
||||
|
||||
this.enabled = true;
|
||||
this.clear = false;
|
||||
this.needsSwap = false;
|
||||
|
||||
};
|
||||
|
||||
RenderPass.prototype = {
|
||||
|
||||
render: function ( renderer, writeBuffer, readBuffer, delta ) {
|
||||
|
||||
this.scene.overrideMaterial = this.overrideMaterial;
|
||||
if ( this.clearColor ) {
|
||||
|
||||
this.oldClearColor.copy( renderer.getClearColor() );
|
||||
this.oldClearAlpha = renderer.getClearAlpha();
|
||||
|
||||
renderer.setClearColor( this.clearColor, this.clearAlpha );
|
||||
|
||||
}
|
||||
|
||||
renderer.render( this.scene, this.camera, readBuffer, this.clear );
|
||||
|
||||
|
||||
if ( this.clearColor ) {
|
||||
|
||||
renderer.setClearColor( this.oldClearColor, this.oldClearAlpha );
|
||||
|
||||
}
|
||||
|
||||
this.scene.overrideMaterial = null;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default RenderPass;
|
|
@ -1,45 +0,0 @@
|
|||
import * as THREE from '../three';
|
||||
|
||||
export default class RenderPass {
|
||||
constructor(cfg) {
|
||||
this.scene;
|
||||
this.camera = cfg.camera;
|
||||
this.renderer = cfg.renderer;
|
||||
this.clearColor = cfg.clear.clearColor;
|
||||
this.clearAlpha = cfg.clear.clearAlpha;
|
||||
this.size = cfg.size ? cfg.size : cfg.renderer.getSize();
|
||||
const defaultRenderCfg = {
|
||||
minFilter: THREE.NearestFilter,
|
||||
magFilter: THREE.NearestFilter,
|
||||
format: THREE.RGBAFormat,
|
||||
stencilBuffer: false,
|
||||
depthBuffer: false
|
||||
};
|
||||
this.renderCfg = cfg.renderCfg ? cfg.renderCfg : defaultRenderCfg;
|
||||
this._init(cfg);
|
||||
}
|
||||
|
||||
_init() {
|
||||
this.scene = new THREE.Scene();
|
||||
this.pass = new THREE.WebGLRenderTarget(this.size.width, this.size.height, this.renderCfg);
|
||||
this.originClearColor = this.renderer.getClearColor();
|
||||
this.originClearAlpha = this.renderer.getClearAlpha();
|
||||
this.texture = this.pass.texture;
|
||||
}
|
||||
|
||||
add(mesh) {
|
||||
this.scene.add(mesh);
|
||||
}
|
||||
|
||||
remove(mesh) {
|
||||
this.scene.remove(mesh);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
this.renderer.setClearColor(this.clearColor, this.clearAlpha);
|
||||
this.renderer.render(this.scene, this.camera, this.pass, true);
|
||||
this.renderer.setRenderTarget(null);
|
||||
this.renderer.setClearColor(this.originClearColor, this.originClearAlpha);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// jscs:disable
|
||||
/* eslint-disable */
|
||||
|
||||
import * as THREE from '../three';
|
||||
|
||||
/**
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
*/
|
||||
|
||||
var ShaderPass = function( shader, textureID ) {
|
||||
|
||||
this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
|
||||
|
||||
if ( shader instanceof THREE.ShaderMaterial ) {
|
||||
|
||||
this.uniforms = shader.uniforms;
|
||||
|
||||
this.material = shader;
|
||||
|
||||
}
|
||||
else if ( shader ) {
|
||||
|
||||
this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
|
||||
|
||||
this.material = new THREE.ShaderMaterial( {
|
||||
|
||||
defines: shader.defines || {},
|
||||
uniforms: this.uniforms,
|
||||
vertexShader: shader.vertexShader,
|
||||
fragmentShader: shader.fragmentShader
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
this.renderToScreen = false;
|
||||
|
||||
this.enabled = true;
|
||||
this.needsSwap = true;
|
||||
this.clear = true;
|
||||
|
||||
|
||||
this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
|
||||
this.scene.add( this.quad );
|
||||
|
||||
};
|
||||
|
||||
ShaderPass.prototype = {
|
||||
|
||||
render: function( renderer, writeBuffer, readBuffer, delta ) {
|
||||
if ( this.uniforms[ this.textureID ] ) {
|
||||
this.uniforms[ this.textureID ].value = readBuffer.texture;
|
||||
|
||||
}
|
||||
renderer.autoClear = false;
|
||||
this.quad.material = this.material;
|
||||
|
||||
if ( this.renderToScreen ) {
|
||||
renderer.render( this.scene, this.camera );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.render( this.scene, this.camera, writeBuffer, this.clear );
|
||||
|
||||
}
|
||||
renderer.autoClear = true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default ShaderPass;
|
|
@ -82,8 +82,14 @@ export default class Layer extends Base {
|
|||
* @param {*} type mesh类型是区别是填充还是边线
|
||||
*/
|
||||
add(object, type = 'fill') {
|
||||
type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object;
|
||||
// composer合图层绘制
|
||||
if (object.type === 'composer') {
|
||||
this._object3D = object;
|
||||
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);
|
||||
|
@ -108,6 +114,12 @@ export default class Layer extends Base {
|
|||
}
|
||||
}
|
||||
remove(object) {
|
||||
if (object.type === 'composer') {
|
||||
this.scene._engine.composerLayers = this.scene._engine.composerLayers.filter(layer => {
|
||||
return (layer !== object);
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._object3D.remove(object);
|
||||
}
|
||||
_getUniqueId() {
|
||||
|
@ -650,6 +662,11 @@ export default class Layer extends Base {
|
|||
*/
|
||||
destroy() {
|
||||
this.removeAllListeners();
|
||||
if (this._object3D.type === 'composer') {
|
||||
this.remove(this._object3D);
|
||||
|
||||
return;
|
||||
}
|
||||
if (this._object3D && this._object3D.children) {
|
||||
let child;
|
||||
for (let i = 0; i < this._object3D.children.length; i++) {
|
||||
|
|
|
@ -47,6 +47,10 @@ export default class Source extends Base {
|
|||
});
|
||||
this._transforms = trans;
|
||||
}
|
||||
transform(option) {
|
||||
const data = getTransform(option.type)(this.data, option);
|
||||
Object.assign(this.data, data);
|
||||
}
|
||||
_projectCoords() {
|
||||
this.data.dataArray.forEach(data => {
|
||||
// data.coordinates = this._coordProject(data.coordinates);
|
||||
|
|
|
@ -18,6 +18,7 @@ export { InstancedBufferGeometry } from 'three/src/core/InstancedBufferGeometry'
|
|||
export { PlaneBufferGeometry } from 'three/src/geometries/PlaneGeometry.js';
|
||||
export { BoxBufferGeometry } from 'three/src/geometries/BoxGeometry.js';
|
||||
export { Raycaster } from 'three/src/core/Raycaster.js';
|
||||
export { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
|
||||
export { Matrix4 } from 'three/src/math/Matrix4.js';
|
||||
export { Matrix3 } from 'three/src/math/Matrix3.js';
|
||||
export { Line } from 'three/src/objects/Line.js';
|
||||
|
|
|
@ -13,6 +13,7 @@ export function HeatmapIntensityMaterial(opt) {
|
|||
vertexShader: vs,
|
||||
fragmentShader: fs,
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
blending: THREE.AdditiveBlending
|
||||
});
|
||||
return material;
|
||||
|
@ -23,7 +24,8 @@ export function HeatmapColorizeMaterial(opt) {
|
|||
const material = new Material({
|
||||
uniforms: {
|
||||
u_texture: { value: opt.texture },
|
||||
u_colorRamp: { value: opt.colorRamp }
|
||||
u_colorRamp: { value: opt.colorRamp },
|
||||
u_opacity: { value: opt.opacity }
|
||||
},
|
||||
vertexShader: vs,
|
||||
fragmentShader: fs,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
uniform sampler2D u_texture;
|
||||
uniform sampler2D u_colorRamp;
|
||||
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));
|
||||
gl_FragColor = color;
|
||||
gl_FragColor.a = color.a * smoothstep(0.,0.05,intensity);
|
||||
gl_FragColor.a = color.a * smoothstep(0.,0.05,intensity) * u_opacity;
|
||||
|
||||
}
|
|
@ -2,7 +2,7 @@ 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 { drawHeatmap, updateIntensityPass } from './render/heatmap/heatmap';
|
||||
import { drawHeatmap } from './render/heatmap/heatmap';
|
||||
import hexagonBuffer from '../geom/buffer/heatmap/hexagon';
|
||||
|
||||
export default class HeatMapLayer extends Layer {
|
||||
|
@ -56,10 +56,10 @@ export default class HeatMapLayer extends Layer {
|
|||
this.add(girdMesh);
|
||||
}
|
||||
|
||||
afterRender() {
|
||||
if (this.shapeType !== 'grid' && this.shapeType !== 'hexagon') {
|
||||
updateIntensityPass(this);
|
||||
}
|
||||
}
|
||||
// afterRender() {
|
||||
// if (this.shapeType !== 'grid' && this.shapeType !== 'hexagon') {
|
||||
// updateIntensityPass(this);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,32 @@
|
|||
import HeatmapBuffer from '../../../geom/buffer/heatmap/heatmap';
|
||||
import { createColorRamp } from '../../../geom/buffer/heatmap/heatmap';
|
||||
import { HeatmapIntensityMaterial, HeatmapColorizeMaterial } from '../../../geom/material/heatmapMateial';
|
||||
import Renderpass from '../../../core/engine/renderpass';
|
||||
// import Renderpass from '../../../core/engine/renderpass.bak';
|
||||
import RenderPass from '../../../core/engine/render-pass';
|
||||
import ShaderPass from '../../../core/engine/shader-pass';
|
||||
import EffectComposer from '../../../core/engine/effect-composer';
|
||||
import * as THREE from '../../../core/three';
|
||||
|
||||
export function drawHeatmap(layer) {
|
||||
const bbox = calBoundingBox(layer.layerData);
|
||||
layer.dataBbox = bbox;
|
||||
|
||||
const colors = layer.get('styleOptions').rampColors;
|
||||
layer.colorRamp = createColorRamp(colors);
|
||||
createIntensityPass(layer, bbox);
|
||||
createColorizePass(layer, bbox);
|
||||
const heatmap = new heatmapPass(layer);
|
||||
const copy = new copyPass(layer);
|
||||
copy.renderToScreen = true;
|
||||
const composer = new EffectComposer(layer.scene._engine._renderer, layer.scene._container);
|
||||
composer.addPass(heatmap);
|
||||
composer.addPass(copy);
|
||||
layer.add(composer);
|
||||
|
||||
}
|
||||
|
||||
function createIntensityPass(layer, bbox) {
|
||||
|
||||
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
|
||||
const buffer = new HeatmapBuffer({
|
||||
data
|
||||
|
@ -27,107 +38,26 @@ function createIntensityPass(layer, bbox) {
|
|||
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));
|
||||
// set material
|
||||
const material = new HeatmapIntensityMaterial({
|
||||
intensity: style.intensity,
|
||||
radius: style.radius,
|
||||
zoom: layer.scene.getZoom()
|
||||
});
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
// set camera
|
||||
const passOrth = new THREE.OrthographicCamera(bbox.width / -2, bbox.width / 2, bbox.height / 2, bbox.height / -2, 1, 10000);
|
||||
passOrth.position.set(bbox.minX + bbox.width / 2, bbox.minY + bbox.height / 2, 1000);
|
||||
// renderpass
|
||||
const renderer = layer.scene._engine._renderer;
|
||||
// get extension for bilinear texture interpolation:https://threejs.org/docs/#api/en/textures/DataTexture
|
||||
/* const gl = renderer.domElement.getContext('webgl') ||
|
||||
renderer.domElement.getContext('experimental-webgl');
|
||||
gl.getExtension('OES_texture_float_linear');*/
|
||||
const renderpass = new Renderpass({
|
||||
renderer,
|
||||
camera: passOrth,
|
||||
size: {
|
||||
width: 2000,
|
||||
height: 2000 * (bbox.height / bbox.width)
|
||||
},
|
||||
clear: {
|
||||
clearColor: 0x000000,
|
||||
clearAlpha: 0.0
|
||||
},
|
||||
renderCfg: {
|
||||
wrapS: THREE.ClampToEdgeWrapping,
|
||||
wrapT: THREE.ClampToEdgeWrapping,
|
||||
minFilter: THREE.LinearFilter,
|
||||
magFilter: THREE.LinearFilter,
|
||||
format: THREE.RGBAFormat,
|
||||
stencilBuffer: false,
|
||||
depthBuffer: false
|
||||
}
|
||||
});
|
||||
renderpass.add(mesh);
|
||||
renderpass.render();
|
||||
layer.intensityPass = renderpass;
|
||||
layer.intensityMesh = mesh;
|
||||
updateIntensityPass(layer);
|
||||
}
|
||||
|
||||
export function updateIntensityPass(layer) {
|
||||
const mesh = layer.intensityMesh;
|
||||
const zoom = layer.scene.getZoom();
|
||||
const bbox = layer.dataBbox;
|
||||
mesh.material.uniforms.u_zoom.value = zoom;
|
||||
const passWidth = Math.min(8000, Math.pow(zoom, 2.0) * 250);
|
||||
const passHeight = passWidth * (bbox.height / bbox.width);
|
||||
layer.intensityPass.pass.setSize(passWidth, passHeight);
|
||||
layer.intensityPass.render();
|
||||
}
|
||||
|
||||
function createColorizePass(layer, bbox) {
|
||||
// create plane geometry
|
||||
const geometery = new THREE.PlaneBufferGeometry(bbox.width, bbox.height);
|
||||
const material = new HeatmapColorizeMaterial({
|
||||
texture: layer.intensityPass.texture,
|
||||
colorRamp: layer.colorRamp
|
||||
});
|
||||
const mesh = new THREE.Mesh(geometery, material);
|
||||
mesh.position.set(bbox.minX + bbox.width / 2, bbox.minY + bbox.height / 2, 0.0);
|
||||
layer.add(mesh);
|
||||
}
|
||||
|
||||
function calBoundingBox(data) {
|
||||
let minX = Infinity;
|
||||
let minY = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let maxY = -Infinity;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const p = data[i].coordinates;
|
||||
if (p[0] < minX) {
|
||||
minX = p[0];
|
||||
} else if (p[0] > maxX) {
|
||||
maxX = p[0];
|
||||
}
|
||||
if (p[1] < minY) {
|
||||
minY = p[1];
|
||||
} else if (p[1] > maxY) {
|
||||
maxY = p[1];
|
||||
}
|
||||
}
|
||||
|
||||
minX -= ((maxX - minX) * 0.5);
|
||||
maxX += ((maxX - minX) * 0.5);
|
||||
minY -= ((maxY - minY) * 0.5);
|
||||
maxY += ((maxY - minY) * 0.5);
|
||||
|
||||
const width = maxX - minX;
|
||||
const height = maxY - minY;
|
||||
|
||||
|
||||
return {
|
||||
minX,
|
||||
maxX,
|
||||
minY,
|
||||
maxY,
|
||||
width,
|
||||
height
|
||||
scene.add(mesh);
|
||||
scene.onBeforeRender = () => { // 每次渲染前改变状态
|
||||
const zoom = layer.scene.getZoom();
|
||||
mesh.material.setUniformsValue('u_zoom', zoom);
|
||||
};
|
||||
const pass = new RenderPass(scene, camera);
|
||||
return pass;
|
||||
}
|
||||
function copyPass(layer) {
|
||||
const style = layer.get('styleOptions');
|
||||
const material = new HeatmapColorizeMaterial({
|
||||
colorRamp: layer.colorRamp,
|
||||
opacity: style.opacity
|
||||
});
|
||||
const copyPass = new ShaderPass(material, 'u_texture');
|
||||
return copyPass;
|
||||
}
|
||||
|
|
|
@ -55,8 +55,6 @@ export default class GaodeMap extends Base {
|
|||
asyncCamera(engine) {
|
||||
this._engine = engine;
|
||||
const camera = engine._camera;
|
||||
const scene = engine._scene;
|
||||
const pickScene = engine._picking._pickingScene;
|
||||
this.map.on('camerachange', e => {
|
||||
const mapCamera = e.camera;
|
||||
let { fov, near, far, height, pitch, rotation, aspect } = mapCamera;
|
||||
|
@ -70,15 +68,16 @@ export default class GaodeMap extends Base {
|
|||
camera.position.z = height * Math.cos(pitch);
|
||||
camera.position.x = height * Math.sin(pitch) * Math.sin(rotation);
|
||||
camera.position.y = -height * Math.sin(pitch) * Math.cos(rotation);
|
||||
|
||||
camera.up.x = -Math.cos(pitch) * Math.sin(rotation);
|
||||
camera.up.y = Math.cos(pitch) * Math.cos(rotation);
|
||||
camera.up.z = Math.sin(pitch);
|
||||
camera.lookAt(0, 0, 0);
|
||||
scene.position.x = -e.camera.position.x;
|
||||
scene.position.y = e.camera.position.y;
|
||||
pickScene.position.x = -e.camera.position.x;
|
||||
pickScene.position.y = e.camera.position.y;
|
||||
camera.position.x += e.camera.position.x;
|
||||
camera.position.y += -e.camera.position.y;
|
||||
// scene.position.x = -e.camera.position.x;
|
||||
// scene.position.y = e.camera.position.y;
|
||||
// pickScene.position.x = -e.camera.position.x;
|
||||
// pickScene.position.y = e.camera.position.y;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -129,6 +128,10 @@ export default class GaodeMap extends Base {
|
|||
scene.setZoom = zoom => {
|
||||
return map.setZoom(zoom);
|
||||
};
|
||||
scene.setZoomAndCenter = (zoom, center) => {
|
||||
const lnglat = new AMap.LngLat(center[0], center[1]);
|
||||
return map.setZoomAndCenter(zoom, lnglat);
|
||||
};
|
||||
scene.setBounds = extent => {
|
||||
return map.setBounds(new AMap.Bounds([ extent[0], extent[1] ], [ extent[2], extent[3] ]));
|
||||
};
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import Supercluster from 'supercluster';
|
||||
export function cluster(data, option) {
|
||||
const { radius = 40, maxZoom = 16, minZoom = 0, field, zoom = 2 } = option;
|
||||
if (data.pointIndex) {
|
||||
const clusterPoint = data.pointIndex.getClusters(data.extent, zoom);
|
||||
data.dataArray = formatData(clusterPoint);
|
||||
return data;
|
||||
}
|
||||
const pointIndex = new Supercluster({
|
||||
radius,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
map: props => ({ sum: props[field] }),
|
||||
reduce: (accumulated, props) => { accumulated.sum += props.sum; }
|
||||
});
|
||||
const geojson = {
|
||||
type: 'FeatureCollection'
|
||||
};
|
||||
geojson.features = data.dataArray.map(item => {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: {
|
||||
[field]: item[field]
|
||||
},
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: item.coordinates
|
||||
}
|
||||
};
|
||||
});
|
||||
pointIndex.load(geojson.features);
|
||||
const clusterPoint = pointIndex.getClusters(data.extent, zoom);
|
||||
const resultData = clusterPoint.map((point, index) => {
|
||||
return {
|
||||
coordinates: point.geometry.coordinates,
|
||||
_id: index + 1,
|
||||
...point.properties
|
||||
};
|
||||
});
|
||||
data.dataArray = resultData;
|
||||
data.pointIndex = pointIndex;
|
||||
return data;
|
||||
}
|
||||
function formatData(clusterPoint) {
|
||||
return clusterPoint.map((point, index) => {
|
||||
return {
|
||||
coordinates: point.geometry.coordinates,
|
||||
_id: index + 1,
|
||||
...point.properties
|
||||
};
|
||||
});
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { expect } from 'chai';
|
||||
import { pointData } from '../../../asset/data/point';
|
||||
import { cluster } from '../../../../src/source/transform/cluster';
|
||||
describe('hexagon Test', function() {
|
||||
|
||||
it('pointToCuster', function() {
|
||||
const dataArray = pointData.map(item => {
|
||||
const lng = 1e-6 * (250 * item.grid_x + 125),
|
||||
lat = 1e-6 * (250 * item.grid_y + 125);
|
||||
return {
|
||||
v: item.count * 1,
|
||||
coordinates: [ lng, lat ]
|
||||
};
|
||||
});
|
||||
|
||||
const data = {
|
||||
dataArray,
|
||||
extent: [ -180, -85, 180, 85 ]
|
||||
};
|
||||
const grid = cluster(data, { radius: 40, field: 'v', zoom: 13 });
|
||||
expect(grid.dataArray.length).eql(26);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue