@orca-fe/x-map
Version:
120 lines (119 loc) • 4.88 kB
JavaScript
/* eslint-disable no-param-reassign */
import { CanvasTexture, FrontSide, Mesh, MeshBasicMaterial, PlaneGeometry } from 'three';
import Supercluster from 'supercluster';
import ThreeObject from './ThreeObject';
import heatmap from './heatmap';
import { lonLat2Mercator, MercatorMax } from '../../utils/coord';
import { debounce } from '../../utils/private';
import ThreeLayer from '../ThreeLayer';
function rangeDiff(min, max, value) {
if (value < min)
return value - min;
if (value > max)
return value - max;
return 0;
}
function toGeoJSONPoint(data) {
return data.map(({ value, lng, lat }) => ({
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [lng, lat],
},
properties: {
value,
lng,
lat,
},
}));
}
export default class HeatmapObject extends ThreeObject {
constructor(options) {
super();
this.material = new MeshBasicMaterial({ side: FrontSide, opacity: 0.5, transparent: true });
this.canvas = document.createElement('canvas');
this.sc = new Supercluster({
maxZoom: 1,
});
this.dom = document.createElement('div');
this.redraw = () => {
var _a;
if ((_a = this.layer) === null || _a === void 0 ? void 0 : _a.map) {
const { map } = this.layer;
const { threeCenter } = map;
// 取得屏幕尺寸
const { zoom } = map.getViewport();
const bounds = map.getBounds();
const [minMercator, maxMercator] = bounds.map(lonLat2Mercator);
const mercatorSize = [maxMercator[0] - minMercator[0], maxMercator[1] - minMercator[1]];
const size = map.getSize();
// 根据屏幕尺寸,计算出热力图 canvas 的尺寸
const scale = ((Math.pow(2, zoom) / MercatorMax) * Math.min(size[0], size[1])) / 20;
const canvasSize = [mercatorSize[0] * scale, mercatorSize[1] * scale];
const [width, height] = canvasSize;
[this.canvas.width, this.canvas.height] = canvasSize;
this.canvas.style.width = `${this.canvas.width}px`;
this.canvas.style.height = `${this.canvas.height}px`;
let radius = Math.round(20 * Math.pow(2, rangeDiff(12, 15, zoom)));
radius = Math.max(2, radius);
const points = this.data.map((p) => {
const [x, y] = lonLat2Mercator([p.x, p.y]);
return {
x: Math.round((x - minMercator[0]) * scale),
y: -Math.round((y - maxMercator[1]) * scale),
value: p.value,
};
});
this.heatmap = heatmap.create({
container: this.dom,
canvas: this.canvas,
radius,
width,
height,
});
this.heatmap.setData({
min: 0,
max: 100,
data: points,
});
this.heatmap.repaint();
// 重新调整 平面的尺寸
const center = [
0.5 * (minMercator[0] + maxMercator[0]) - threeCenter[0],
0.5 * (minMercator[1] + maxMercator[1]) - threeCenter[1],
];
if (this.material.map)
this.material.map.needsUpdate = true;
this.object3D.position.set(center[0], center[1], 1);
this.object3D.scale.set(Math.abs(maxMercator[0] - minMercator[0]), Math.abs(maxMercator[1] - minMercator[1]), 1);
if (this.layer instanceof ThreeLayer) {
this.layer.three.render();
}
}
};
this.redrawDebounce = debounce(this.redraw, 300, { maxWait: 1000 });
const { data = [] } = options;
this.data = data.map(({ lng, lat, value }) => ({
x: lng,
y: lat,
value,
}));
this.sc.load(toGeoJSONPoint(data));
this.object3D = new Mesh(new PlaneGeometry(1, 1, 2), this.material);
this.material.map = new CanvasTexture(this.canvas);
document.body.append(this.dom);
this.dom.style.display = 'none';
this.dom.append(this.canvas);
this.heatmap = heatmap.create({
container: this.dom,
canvas: this.canvas,
radius: 10,
});
}
updatePosition() {
this.redrawDebounce();
}
createObject() {
this.redraw();
}
}