UNPKG

@orca-fe/x-map

Version:
120 lines (119 loc) 4.88 kB
/* 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(); } }