antv-l7/stories/3D_Model/Components/threejsmap.tsx

297 lines
8.2 KiB
TypeScript
Raw Normal View History

// @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';
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,
},
];
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({
center: [120.5, 24],
pitch: 60,
rotation: 0,
zoom: 8.2,
style: 'dark',
}),
});
this.scene = scene;
scene.registerRenderService(ThreeRender);
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;
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() {
gl_FragColor = vec4(0.92549, 1.0, 0.91372549, 1.0 - h/${h}.0);
}`;
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]);
mesh.renderOrder = -1;
threeScene.add(mesh);
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));
});
},
})
.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,
}}
/>
);
}
}