做一个简单的地图轮廓

228 阅读3分钟

涉及到的知识点

  • 平面填充几何体ShapeGeometry
  • 包围盒Box3
  • 正投影相机OrthographicCamera

目标效果

image.png

实现步骤

准备好地图数据

// 河南边界轮廓边界经纬度坐标
export default [
  [110.3906, 34.585],
  [110.8301, 34.6289],
  [111.1816, 34.8047],
  [111.5332, 34.8486],
  [111.7969, 35.0684],
  [112.0605, 35.0684],
  [112.0605, 35.2881],
  [112.7637, 35.2002],
  [113.1152, 35.332],
  [113.6426, 35.6836],
  [113.7305, 36.3428],
  [114.873, 36.123],
  [114.9609, 36.0791],
  [115.1367, 36.2109],
  [115.3125, 36.0791],
  [115.4883, 36.167],
  [115.3125, 35.8154],
  [116.1035, 36.0791],
  [115.4883, 35.7275],
  [115.2246, 35.4199],
  [115.0488, 35.376],
  [114.7852, 35.0684],
  [115.4004, 34.8486],
  [115.5762, 34.585],
  [116.1914, 34.585],
  [116.1914, 34.4092],
  [116.543, 34.2773],
  [116.6309, 33.9258],
  [116.1914, 33.7061],
  [116.0156, 33.9697],
  [115.6641, 34.0576],
  [115.5762, 33.9258],
  [115.5762, 33.6621],
  [115.4004, 33.5303],
  [115.3125, 33.1787],
  [114.873, 33.1348],
  [114.873, 33.0029],
  [115.1367, 32.8711],
  [115.2246, 32.6074],
  [115.5762, 32.4316],
  [115.8398, 32.5195],
  [115.9277, 31.7725],
  [115.4883, 31.6846],
  [115.4004, 31.4209],
  [115.2246, 31.4209],
  [115.1367, 31.5967],
  [114.7852, 31.4648],
  [114.6094, 31.5527],
  [114.5215, 31.7725],
  [114.1699, 31.8604],
  [113.9941, 31.7725],
  [113.8184, 31.8604],
  [113.7305, 32.4316],
  [113.4668, 32.2998],
  [113.2031, 32.4316],
  [112.3242, 32.3438],
  [111.5332, 32.6074],
  [111.0059, 33.2666],
  [111.0059, 33.5303],
  [110.6543, 33.8379],
  [110.6543, 34.1455],
  [110.4785, 34.2334],
  [110.3906, 34.585]
]

1. 将地图每个坐标数据包装成二维向量,并放入一个数组中

// 一组二维向量表示一个多边形轮廓坐标
const pointsArr = [];


 // data坐标数据转化为Vector2构成的顶点数组
data.forEach(function(e){
    const v2 = new THREE.Vector2(e[0],e[1])
    pointsArr.push(v2);
})

2. 定义一个平面多边形轮廓,参数为一个二位向量数组

// Shape表示一个平面多边形轮廓,参数是二维向量构成的数组pointsArr
const shape = new THREE.Shape(pointsArr);

3. 填充轮廓,参数为一个平面多边形轮廓

// 多边形shape轮廓作为ShapeGeometry参数,生成一个多边形平面几何体

const geometry = new THREE.ShapeGeometry(shape);

4. 定义材质,并将材质和模型添加到网格模型中

//定义材质
const material = new THREE.MeshLambertMaterial({

    color: 0x00ffff, //颜色
    
    side: THREE.DoubleSide  //单面还是双面可见
});

//将模型和材质添加进网格对象中
const mesh = new THREE.Mesh(geometry, material);

将mesh添加进场景中

效果图:

image.png

可以看到,地图轮廓已经可以呈现出来了,但是并不在视图中心位置并且很小。接下来便是解决这些问题。

将地图渲染到视图中心

使用包围盒(BOX3)得到地图的尺寸,中心点位置等

// 包围盒计算模型对象的大小和位置
//定义一个包围盒
const box3 = new THREE.Box3();

//使用 expandByObject方法计算出mesh的最小包围盒
box3.expandByObject(mesh); 

//获取到地图的包围盒尺寸
const size = new THREE.Vector3();
box3.getSize(size); 

//获取到地图的中心点坐标
const center = new THREE.Vector3();
box3.getCenter(center); 

得到地图的尺寸坐标和中心点坐标之后,去改变相机的参数。

改变相机指向位置 (LookAt)

//x,y 为获取到的地图中心点
const x = 113.51,y = 33.88;

//与观察点x、y一样,地图平行与canvas画布,相机位置垂直于地图
camera.position.set(x, y, 300);

//相机看向地图中心点
camera.lookAt(x, y, 0);

效果:

image.png

将辅助坐标系的原点设置为地图中心点

const axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper);

//自定义原点
axesHelper.position.set(x, y, 0);

效果:

image.png

设置正投影相机的参数

此处使用的是正投影相机而不是透视投影相机,因为此处不需要模拟人眼效果。

// 正投影相机
const width = window.innerWidth; //canvas画布宽度
const height = window.innerHeight; //canvas画布高度

const k = width / height; //canvas画布宽高比

const s = 2.5;//根据包围盒大小调整s,包围盒y方向尺寸的一半左右

const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 8000);

camera.position.set(x, y, 300);//与观察点x、y一样,地图平行与canvas画布
camera.lookAt(x, y, 0);

效果:

image.png

总结

整体思路为:

  1. 先根据地图数据渲染出地图
  2. 利用包装盒(Box3)得到地图的尺寸,中心位置数据。
  3. 根据地图的尺寸,中心位置等信息改变 相机的参数和辅助坐标系的原点

注意点

当改变相机的指向坐标时,如果有使用相机空间 orbitControls, 需要设置:

controls.target.set(x, y, 0); //与lookAt参数保持一致
controls.update();//update()函数内会执行camera.lookAt(controls.target)

这样改变camera的lookAt 之后,才会生效!