Three.js地图轮廓分割效果
- 基于GeoJson数据解析绘制,支持多级国家/省/市细粒度路径绘制
- 可以通过Shader/材质/贴图实现不同的轮廓分割效果
相关方法
// 经纬度与世界坐标转换
const getPosition = (lng, lat, radius) => {
const phi = (180 + lng) * (Math.PI / 180);
const theta = (90 - lat) * (Math.PI / 180);
return {
x: -radius * Math.sin(theta) * Math.cos(phi),
y: radius * Math.cos(theta),
z: radius * Math.sin(theta) * Math.sin(phi),
};
};
// 解析出GeoJson中所有的图形集合
const parseGeoJson = ({ features }) => {
if (!features.length) return [];
return features.reduce((areas, item) => {
const { coordinates } = item.geometry;
return areas.concat(
coordinates.reduce((careas, citem) => {
return careas.concat(citem);
}, [])
);
}, []);
};
// 绘制地球
const drawEarth = (radius) => {
const geometry = new THREE.SphereGeometry(radius, 50, 50);
const texture = new THREE.TextureLoader().load("./img/earth.jpeg");
const earthMat = new THREE.MeshStandardMaterial({
transparent: false,
depthWrite: true,
map: texture,
roughness: 0.6,
});
return new THREE.Mesh(geometry, earthMat);
};
// 绘制球形路径
const drawEarthWall = (radius, geoJson, height = 1) => {
// 解析路径数据
const positionArrays = parseGeoJson(geoJson);
// 构建透明墙体材质
const material = createFlowWallMat({});
const earthWallGroup = new THREE.Group();
positionArrays.forEach((item) => {
const path = item.reduce((arr, [lng, lat]) => {
const p1 = getPosition(lng, lat, radius);
const p2 = getPosition(lng, lat, radius + height);
return arr.concat([
[
[p1.x, p1.y, p1.z],
[p2.x, p2.y, p2.z],
],
]);
}, []);
const wallMesh = creatWallByPath({ path, material, expand: false });
earthWallGroup.add(wallMesh);
});
return earthWallGroup;
};
使用方法
creatWallByPath
,createFlowWallMat
方法参考前面的文章
import CQgeoJson from "./data/cq_geo_json.js";
import CHINAgeoJson from "./data/china_geo_json.js";
import { creatWallByPath, createFlowWallMat } from "./js/effect.js";
// 地球半径
const radius = 50;
// 绘制地球
const earthMesh = drawEarth(radius);
// 绘制中国地图轮廓
const chinaWallMesh = drawEarthWall(radius, CHINAgeoJson, 3);
// 绘制重庆轮廓
const cqWallMesh = drawEarthWall(radius, CQgeoJson, 2);
scene.add(earthMesh);
scene.add(cqWallMesh);
scene.add(chinaWallMesh);