2021-11-19 20:12:48 +08:00
|
|
|
|
// @ts-ignore
|
|
|
|
|
import { Scene } from '@antv/l7';
|
|
|
|
|
import { GaodeMap, Mapbox } from '@antv/l7-maps';
|
|
|
|
|
import { ThreeLayer, ThreeRender } from '@antv/l7-three';
|
|
|
|
|
import * as React from 'react';
|
|
|
|
|
import * as THREE from 'three';
|
|
|
|
|
|
2021-11-23 19:58:30 +08:00
|
|
|
|
const points = [
|
|
|
|
|
[120.44353624, 22.44126139],
|
|
|
|
|
[120.64050836, 22.24136664],
|
|
|
|
|
[120.70155111, 21.92708306],
|
|
|
|
|
[120.87288601, 21.89738661],
|
|
|
|
|
[120.91492974, 22.30271186],
|
|
|
|
|
[121.03327772, 22.65070394],
|
|
|
|
|
[121.32468397, 22.94568359],
|
|
|
|
|
[121.47954183, 23.3223055],
|
|
|
|
|
[121.64384793, 24.09772976],
|
|
|
|
|
[121.80915717, 24.33907227],
|
|
|
|
|
[121.89250353, 24.6179267],
|
|
|
|
|
[121.84504259, 24.83625488],
|
|
|
|
|
[122.01220106, 25.00145101],
|
|
|
|
|
[121.91709391, 25.13789361],
|
|
|
|
|
[121.62302734, 25.29467556],
|
|
|
|
|
[121.02468424, 25.04048774],
|
|
|
|
|
[120.82376736, 24.68830973],
|
|
|
|
|
[120.68867757, 24.60069417],
|
|
|
|
|
[120.24576172, 23.84053168],
|
|
|
|
|
[120.10276177, 23.70096951],
|
|
|
|
|
[120.10784668, 23.34126356],
|
|
|
|
|
[120.02320774, 23.10765544],
|
|
|
|
|
[120.13473334, 22.99400635],
|
|
|
|
|
[120.29720052, 22.53133111],
|
|
|
|
|
[120.44353624, 22.44126139],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
function iniCylinder(size: number, height: number) {
|
|
|
|
|
const geometry = new THREE.CylinderGeometry(size, size, height, 32);
|
|
|
|
|
const material = new THREE.MeshBasicMaterial({
|
|
|
|
|
color: '#0ff',
|
|
|
|
|
transparent: true,
|
|
|
|
|
opacity: 0.5,
|
|
|
|
|
depthTest: false,
|
|
|
|
|
});
|
|
|
|
|
const cylinder = new THREE.Mesh(geometry, material);
|
|
|
|
|
return cylinder;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function initPlane(text: string, src: string) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const image = new Image();
|
|
|
|
|
image.src = src;
|
|
|
|
|
image.crossOrigin = 'none';
|
|
|
|
|
image.onload = function() {
|
|
|
|
|
const width = image.width;
|
|
|
|
|
const height = image.height;
|
|
|
|
|
const canvas = document.createElement('canvas');
|
|
|
|
|
canvas.width = width * 10;
|
|
|
|
|
canvas.height = height * 10;
|
|
|
|
|
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
|
|
|
|
ctx.globalAlpha = 0.4;
|
|
|
|
|
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
|
|
|
ctx.globalAlpha = 1;
|
|
|
|
|
ctx.fillStyle = 'rgb(30, 160, 30)';
|
|
|
|
|
ctx.font = `${canvas.height / 3}px bold PingFang-SC-Bold`;
|
|
|
|
|
ctx.textAlign = 'center';
|
|
|
|
|
ctx.textBaseline = 'middle';
|
|
|
|
|
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
|
|
|
|
|
|
|
|
|
|
const texture = new THREE.CanvasTexture(canvas);
|
|
|
|
|
const size = 320000;
|
|
|
|
|
const planeGeometry = new THREE.PlaneBufferGeometry(
|
|
|
|
|
size,
|
|
|
|
|
(size * height) / width,
|
|
|
|
|
1,
|
|
|
|
|
);
|
|
|
|
|
const material = new THREE.MeshBasicMaterial({
|
|
|
|
|
color: 0xffff00,
|
|
|
|
|
side: THREE.DoubleSide,
|
|
|
|
|
map: texture,
|
|
|
|
|
blending: THREE.AdditiveBlending,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const plane = new THREE.Mesh(planeGeometry, material);
|
|
|
|
|
plane.renderOrder = 10;
|
|
|
|
|
|
|
|
|
|
resolve(plane);
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const taiwancity = [
|
|
|
|
|
{
|
|
|
|
|
// 台北
|
|
|
|
|
lng: 121.5,
|
|
|
|
|
lat: 25.05,
|
|
|
|
|
population: 2602418,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// 台中
|
|
|
|
|
lng: 120.5804443359,
|
|
|
|
|
lat: 24.2,
|
|
|
|
|
population: 2820787,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// 台南
|
|
|
|
|
lng: 120.193176269531,
|
|
|
|
|
lat: 22.9963233068,
|
|
|
|
|
population: 1874917,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// 高雄
|
|
|
|
|
lng: 120.30578613281,
|
|
|
|
|
lat: 22.62415215809,
|
|
|
|
|
population: 2765932,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// 桃园
|
|
|
|
|
lng: 121.15,
|
|
|
|
|
lat: 24.9,
|
|
|
|
|
population: 2268807,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
2021-11-19 20:12:48 +08:00
|
|
|
|
export default class Threemap extends React.Component {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
private scene: Scene;
|
|
|
|
|
|
|
|
|
|
public componentWillUnmount() {
|
|
|
|
|
this.scene.destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async componentDidMount() {
|
|
|
|
|
const scene = new Scene({
|
|
|
|
|
id: 'map',
|
|
|
|
|
map: new GaodeMap({
|
2021-11-23 19:58:30 +08:00
|
|
|
|
center: [120.5, 24],
|
|
|
|
|
pitch: 60,
|
|
|
|
|
rotation: 0,
|
|
|
|
|
zoom: 8.2,
|
|
|
|
|
style: 'dark',
|
2021-11-19 20:12:48 +08:00
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
this.scene = scene;
|
2021-11-23 19:58:30 +08:00
|
|
|
|
|
2021-11-19 20:12:48 +08:00
|
|
|
|
scene.registerRenderService(ThreeRender);
|
2021-11-23 19:58:30 +08:00
|
|
|
|
|
|
|
|
|
const img1 =
|
|
|
|
|
'https://gw.alipayobjects.com/mdn/rms_23a451/afts/img/A*zMw0T6gEIZYAAAAAAAAAAAAAARQnAQ';
|
|
|
|
|
|
|
|
|
|
let plane0 = (await initPlane('台北:260.24万', img1)) as THREE.Object3D;
|
|
|
|
|
let plane1 = (await initPlane('台中:282.07万', img1)) as THREE.Object3D;
|
|
|
|
|
let plane2 = (await initPlane('台南:187.49万', img1)) as THREE.Object3D;
|
|
|
|
|
let plane3 = (await initPlane('高雄:276.59万', img1)) as THREE.Object3D;
|
|
|
|
|
let plane4 = (await initPlane('桃园:266.88万', img1)) as THREE.Object3D;
|
2021-11-19 20:12:48 +08:00
|
|
|
|
scene.on('loaded', () => {
|
|
|
|
|
const threeJSLayer = new ThreeLayer({
|
|
|
|
|
enableMultiPassRenderer: false,
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
onAddMeshes: (threeScene: THREE.Scene, layer: ThreeLayer) => {
|
|
|
|
|
threeScene.add(new THREE.AmbientLight(0xffffff));
|
|
|
|
|
const sunlight = new THREE.DirectionalLight(0xffffff, 0.25);
|
|
|
|
|
sunlight.position.set(0, 80000000, 100000000);
|
|
|
|
|
sunlight.matrixWorldNeedsUpdate = true;
|
|
|
|
|
threeScene.add(sunlight);
|
|
|
|
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
let lnglatPoints = points.map((p) => layer.lnglatToCoord(p));
|
|
|
|
|
|
|
|
|
|
const shape = new THREE.Shape();
|
|
|
|
|
|
|
|
|
|
shape.moveTo(lnglatPoints[0][0], lnglatPoints[0][1]);
|
|
|
|
|
for (let i = 1; i < lnglatPoints.length; i++) {
|
|
|
|
|
shape.lineTo(lnglatPoints[i][0], lnglatPoints[i][1]);
|
|
|
|
|
}
|
|
|
|
|
shape.lineTo(lnglatPoints[0][0], lnglatPoints[0][1]);
|
|
|
|
|
|
|
|
|
|
const h = 100000;
|
|
|
|
|
const extrudeSettings = {
|
|
|
|
|
steps: 2,
|
|
|
|
|
depth: h,
|
|
|
|
|
bevelEnabled: true,
|
|
|
|
|
bevelThickness: 1,
|
|
|
|
|
bevelSize: 1,
|
|
|
|
|
bevelOffset: 0,
|
|
|
|
|
bevelSegments: 1,
|
|
|
|
|
};
|
|
|
|
|
const v0 = `
|
|
|
|
|
void main() {
|
|
|
|
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
|
|
|
}`;
|
|
|
|
|
|
|
|
|
|
const f0 = `
|
|
|
|
|
void main() {
|
|
|
|
|
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
|
|
|
|
}`;
|
|
|
|
|
|
|
|
|
|
const v = `
|
|
|
|
|
varying vec2 vUv;
|
|
|
|
|
varying float h;
|
|
|
|
|
void main() {
|
|
|
|
|
vUv = uv;
|
|
|
|
|
h = position.z;
|
|
|
|
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
|
|
|
}`;
|
|
|
|
|
|
|
|
|
|
const f = `
|
|
|
|
|
varying vec2 vUv;
|
|
|
|
|
varying float h;
|
|
|
|
|
void main() {
|
2021-11-23 19:58:30 +08:00
|
|
|
|
gl_FragColor = vec4(0.92549, 1.0, 0.91372549, 1.0 - h/${h}.0);
|
2021-11-19 20:12:48 +08:00
|
|
|
|
}`;
|
|
|
|
|
|
|
|
|
|
const material = new THREE.ShaderMaterial({
|
|
|
|
|
transparent: true,
|
|
|
|
|
opacity: 0,
|
|
|
|
|
depthWrite: false,
|
|
|
|
|
vertexShader: v0,
|
|
|
|
|
fragmentShader: f0,
|
|
|
|
|
});
|
|
|
|
|
const shader_material = new THREE.ShaderMaterial({
|
|
|
|
|
transparent: true,
|
|
|
|
|
vertexShader: v,
|
|
|
|
|
fragmentShader: f,
|
|
|
|
|
side: THREE.DoubleSide, // 双面可见
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
|
|
|
|
const mesh = new THREE.Mesh(geometry, [material, shader_material]);
|
2021-11-23 19:58:30 +08:00
|
|
|
|
mesh.renderOrder = -1;
|
2021-11-19 20:12:48 +08:00
|
|
|
|
threeScene.add(mesh);
|
2021-11-23 19:58:30 +08:00
|
|
|
|
|
|
|
|
|
let planes = [plane0, plane1, plane2, plane3, plane4];
|
|
|
|
|
|
|
|
|
|
taiwancity.map(
|
|
|
|
|
(item: { lng: number; lat: number; population: number }, index) => {
|
|
|
|
|
let cylinderSize = item.population / 50;
|
|
|
|
|
let cylinderHeight = item.population / 10;
|
|
|
|
|
|
|
|
|
|
planes[index].rotation.x = Math.PI / 2;
|
|
|
|
|
layer.setObjectLngLat(
|
|
|
|
|
planes[index],
|
|
|
|
|
[item.lng, item.lat],
|
|
|
|
|
cylinderHeight * 1.5,
|
|
|
|
|
);
|
|
|
|
|
threeScene.add(planes[index]);
|
|
|
|
|
|
|
|
|
|
let cylinder = iniCylinder(cylinderSize, cylinderHeight);
|
|
|
|
|
cylinder.rotation.x = Math.PI / 2;
|
|
|
|
|
layer.setObjectLngLat(
|
|
|
|
|
cylinder,
|
|
|
|
|
[item.lng, item.lat],
|
|
|
|
|
cylinderHeight / 2,
|
|
|
|
|
);
|
|
|
|
|
threeScene.add(cylinder);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
layer.setUpdate(() => {
|
|
|
|
|
let z = layer.getRenderCamera().rotation.z;
|
|
|
|
|
planes.map((p) => (p.rotation.y = z));
|
|
|
|
|
});
|
2021-11-19 20:12:48 +08:00
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
.source({
|
|
|
|
|
type: 'FeatureCollection',
|
|
|
|
|
features: [
|
|
|
|
|
{
|
|
|
|
|
type: 'Feature',
|
|
|
|
|
properties: {},
|
|
|
|
|
geometry: {
|
|
|
|
|
type: 'Point',
|
|
|
|
|
coordinates: [111.4453125, 32.84267363195431],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
})
|
|
|
|
|
.animate(true);
|
|
|
|
|
scene.addLayer(threeJSLayer);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public render() {
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
id="map"
|
|
|
|
|
style={{
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: 0,
|
|
|
|
|
left: 0,
|
|
|
|
|
right: 0,
|
|
|
|
|
bottom: 0,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|