Threejs实现绘制地球,地理位置标注、经纬度转换世界坐标threejs坐标

简介: Threejs实现绘制地球,地理位置标注、经纬度转换世界坐标threejs坐标

1,介绍


该示例使用的是 r95版本Three.js库。

主要实现功能:绘制地球和地理位置进行标注

效果图如下:

2,主要说明


准备一张地图,创建一个球体并进行贴图,把地理位置经纬度转换成threejs的世界坐标,并进行标注。


创建地球,部分代码如下:

// 创建地球 半径100
function createEarth() {
  var earthGeo = new THREE.SphereGeometry(radius, 50, 50);
  var earthMater = new THREE.MeshPhongMaterial({
    map: new THREE.TextureLoader().load('assets/earth/earth3.jpg'),
    transparent: true,
    depthWrite: false,
    side: THREE.DoubleSide,
    blending: THREE.AdditiveBlending,
    opacity: 0.8,
    color: 0x03d98e
  });
  var earthMesh = new THREE.Mesh(earthGeo, earthMater);
  scene.add(earthMesh)
}


经纬度转换成threejs坐标(也叫右手坐标系)

phi是方位面(水平面)内的角度,范围0~360度,theta是俯仰面(竖直面)内的角度,范围0~180度、就是空间极坐标系中的两个参数,和类比直角坐标系里的xyz


threejs实现转换,代码如下:

// 坐标转换,
function createPosition(lnglat) {
  let spherical = new THREE.Spherical
  spherical.radius = radius;
  const lng = lnglat[0]
  const lat = lnglat[1]
  const theta = (lng + 90) * (Math.PI / 180)
  const phi = (90 - lat) * (Math.PI / 180)
  spherical.phi = phi; // phi是方位面(水平面)内的角度,范围0~360度
  spherical.theta = theta; // theta是俯仰面(竖直面)内的角度,范围0~180度
  let position = new THREE.Vector3()
  position.setFromSpherical(spherical)
  return position
}


3,源码


<!DOCTYPE html>
<html>
  <head>
    <title>Threejs实现绘制地球,地理位置标注-经纬度转换世界坐标</title>
    <script type="text/javascript" src="libs/three.js"></script>
    <script type="text/javascript" src="libs/OrbitControls.js"></script>
    <style>
      body {
        margin: 0;
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <div id="dom"></div>
    <script type="text/javascript">
      var camera;
      var renderer;
      var radius = 100; // 地球半径
      var areas = [{
        name: "中国",
        position: [116.20, 39.55]
      }, {
        name: "中非共和国",
        position: [18.35, 4.23]
      }, {
        name: "智利",
        position: [-70.40, -33.24]
      }, {
        name: "乍得",
        position: [14.59, 12.10]
      }, {
        name: "赞比亚",
        position: [28.16, -15.28]
      }, {
        name: "越南",
        position: [105.55, 21.05]
      }, {
        name: "约旦",
        position: [35.52, 31.57]
      }, {
        name: "英属维尔京群岛",
        position: [-64.37, 18.27]
      }, {
        name: "英国",
        position: [-0.05, 51.36]
      }];
      function init() {
        // 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。
        var scene = new THREE.Scene();
        var urls = [
          'assets/textures/cubemap/flowers/posx.jpg',
          'assets/textures/cubemap/flowers/negx.jpg',
          'assets/textures/cubemap/flowers/posy.jpg',
          'assets/textures/cubemap/flowers/negy.jpg',
          'assets/textures/cubemap/flowers/posz.jpg',
          'assets/textures/cubemap/flowers/negz.jpg'
        ];
        var cubeLoader = new THREE.CubeTextureLoader();
        // scene.background = cubeLoader.load(urls);
        // 创建一个摄像机,它定义了我们正在看的地方
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
        // 将摄像机对准场景的中心
        camera.position.z = 500;
        camera.lookAt(scene.position);
        var orbit = new THREE.OrbitControls(camera);
        // 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景
        renderer = new THREE.WebGLRenderer({
          antialias: true,
          logarithmicDepthBuffer: true,
        });
        renderer.setSize(window.innerWidth, window.innerHeight);
        // scene.add(new THREE.AmbientLight(0x666666));
        var ambientLight = new THREE.AmbientLight("#ffffff", 1);
        scene.add(ambientLight);
        // 在屏幕上显示坐标轴
        var axes = new THREE.AxisHelper(radius);
        scene.add(axes);
        // 将平面添加到场景中
        var plane = createPlaneGeometryBasicMaterial();
        // scene.add(plane);
        // initSphere(10, 0, 10);
        createEarth();
        createAreaPoint();
        var rs = coordinateWorldTurnScreen(10, 0, 10);
        var div = document.createElement('div');
        div.innerHTML = '立';
        div.style.padding = '5px';
        div.style.position = 'absolute';
        div.style.backgroundColor = 'rgba(155,0,155,0.8)';
        document.body.appendChild(div);
        div.style.left = rs.x + "px";
        div.style.top = rs.y + "px";
        console.log(rs)
        // 将呈现器的输出添加到HTML元素
        document.getElementById("dom").appendChild(renderer.domElement);
        // 启动动画
        renderScene();
        /**
         * 创建地面并添加材质
         * wrapS属性定义的是纹理沿x轴方向的行为,而warpT属性定义的是纹理沿y轴方向的行为。
         * Three.js为这些属性提供了如下两个选项:
         * ·THREE.RepeatWrapping允许纹理重复自己。
         * ·THREE.ClampToEdgeWrapping是属性的默认值。
         * 属性值为THREE.ClampToEdgeWrapping时,那么纹理的整体不会重复,只会重复纹理边缘的像素来填满剩下的空间。
         */
        function createPlaneGeometryBasicMaterial() {
          var textureLoader = new THREE.TextureLoader();
          var cubeMaterial = new THREE.MeshStandardMaterial({
            map: textureLoader.load("assets/textures/stone/cd.jpg"),
          });
          cubeMaterial.map.wrapS = THREE.RepeatWrapping;
          cubeMaterial.map.wrapT = THREE.RepeatWrapping;
          cubeMaterial.map.repeat.set(8, 8)
          // 创建地平面并设置大小
          var planeGeometry = new THREE.PlaneGeometry(100, 100);
          var plane = new THREE.Mesh(planeGeometry, cubeMaterial);
          // 设置平面位置并旋转
          plane.rotation.x = -0.5 * Math.PI;
          plane.position.x = 0;
          plane.position.z = 0;
          return plane;
        }
        // 世界坐标转屏幕坐标
        function coordinateWorldTurnScreen(x, y, z) {
          let world_vector = new THREE.Vector3(x, y, z);
          let vector = world_vector.project(camera);
          let halfWidth = window.innerWidth / 2,
            halfHeight = window.innerHeight / 2;
          return {
            x: Math.round(vector.x * halfWidth + halfWidth),
            y: Math.round(-vector.y * halfHeight + halfHeight)
          }
        }
        // 创建地球 半径100
        function createEarth() {
          var earthGeo = new THREE.SphereGeometry(radius, 50, 50);
          var earthMater = new THREE.MeshPhongMaterial({
            map: new THREE.TextureLoader().load('assets/earth/earth3.jpg'),
            transparent: true,
            depthWrite: false,
            side: THREE.DoubleSide,
            blending: THREE.AdditiveBlending,
            opacity: 0.8,
            color: 0x03d98e
          });
          var earthMesh = new THREE.Mesh(earthGeo, earthMater);
          scene.add(earthMesh)
        }
        function createAreaPoint() {
          // 球面
          let sphereGeom = new THREE.SphereGeometry(1, 20, 20),
            sphereMat = new THREE.MeshBasicMaterial({
              color: 0x03d98e,
              wireframe: true
            })
          let sphere = new THREE.Mesh(sphereGeom, sphereMat)
          scene.add(sphere)
          // 地标及光锥
          for (let i = 0, length = areas.length; i < length; i++) {
            const position = createPosition(this.areas[i].position)
            createHexagon(position); // 地标
          }
        }
        // 坐标转换,
        function createPosition(lnglat) {
          let spherical = new THREE.Spherical
          spherical.radius = radius;
          const lng = lnglat[0]
          const lat = lnglat[1]
          const theta = (lng + 90) * (Math.PI / 180)
          const phi = (90 - lat) * (Math.PI / 180)
          spherical.phi = phi; // phi是方位面(水平面)内的角度,范围0~360度
          spherical.theta = theta; // theta是俯仰面(竖直面)内的角度,范围0~180度
          let position = new THREE.Vector3()
          position.setFromSpherical(spherical)
          return position
        }
        // 创建地标标记
        function createHexagon(position) {
          var hexagon = new THREE.Object3D()
          let hexagonLine = new THREE.CircleGeometry(4, 6)
          let hexagonPlane = new THREE.CircleGeometry(3, 6)
          let vertices = hexagonLine.vertices
          vertices.shift() // 第一个节点是中心点
          let material = new THREE.MeshBasicMaterial({
            color: 0xffff00,
            side: THREE.DoubleSide,
            opacity: 0.5
          })
          let circleLine = new THREE.LineLoop(hexagonLine, material)
          let circlePlane = new THREE.Mesh(hexagonPlane, material)
          circleLine.position.copy(position)
          circlePlane.position.copy(position)
          circlePlane.lookAt(new THREE.Vector3(0, 0, 0))
          circleLine.lookAt(new THREE.Vector3(0, 0, 0))
          hexagon.add(circleLine)
          hexagon.add(circlePlane)
          scene.add(hexagon);
        }
        // 初始球体
        function initSphere(x, y, z) {
          var geometry = new THREE.SphereGeometry(1, 100, 100); //球体几何
          var material = new THREE.MeshBasicMaterial({
            color: 0xffff00
          }); //网格基础材料
          var sphere = new THREE.Mesh(geometry, material);
          sphere.position.x = x;
          sphere.position.y = y;
          sphere.position.z = z;
          scene.add(sphere);
        }
        function renderScene() {
          orbit.update();
          // 使用requestAnimationFrame函数进行渲染
          requestAnimationFrame(renderScene);
          renderer.render(scene, camera);
        }
        // 渲染的场景
        renderer.render(scene, camera);
      }
      window.onload = init;
      // 随着窗体的变化修改场景
      function onResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
      }
      // 监听窗体调整大小事件
      window.addEventListener('resize', onResize, false);
    </script>
  </body>
</html>
目录
相关文章
|
7月前
|
定位技术
百度地图缩放级别与比例尺的关系
百度地图缩放级别与比例尺的关系
546 0
|
定位技术
99Echarts - 地理坐标/地图(Hexagonal Binning)
99Echarts - 地理坐标/地图(Hexagonal Binning)
70 0
|
JSON JavaScript 数据可视化
D3 不到20行代码就能实现世界地图的绘制
每到农历年末,相信很多小伙伴和本作者一样,都忍不住会去看江苏卫视的一档脑力比拼节目《最强大脑》,尽管上一季最强大脑喷点确实很多,但依旧没有减弱"追剧"的热情。今年最强大脑(第5季)的赛制有很大的变化,挑战的人数从百人大战,到最强30脑,再到现从第三场的一对一PK,确实与以往有了很大的不同。此外,今年更加强调了选手在生活中的光环,例如本文要引用的一场比赛就是最近一期来自清华的孙勇与北京的陈泽坤的一场以地图投影为背景的比赛,孙勇就是顶着2016安徽省高考理科状元的光环来的。今年没了叨叨魏,节目的流程显得自然了很多。好了,不扯了,来、来、来来来!我们开始说本文要讲的主题--地图。
1455 0
D3 不到20行代码就能实现世界地图的绘制
|
4月前
【threejs教程】让你的场景贴图变得多姿多彩:UV坐标详解
【8月更文挑战第6天】threejs教程:让你的场景贴图变得多姿多彩,UV坐标详解
216 5
【threejs教程】让你的场景贴图变得多姿多彩:UV坐标详解
|
4月前
|
JavaScript 前端开发 定位技术
GIS开发:开源库计算经纬度坐标和瓦片坐标
GIS开发:开源库计算经纬度坐标和瓦片坐标
109 0
|
算法 JavaScript 前端开发
84坐标系、02坐标系、百度坐标之间相互转换算法
最近有同学反馈之前的坐标系转换有问题,对之前的工具类进行了修正。 一、地图坐标转换java工具类 包含84坐标系、02坐标系、百度地图、高德地图、腾讯地图坐标之间相互转换的算法 wgs84ToGcj02:将 WGS84 坐标系下的经纬度转换为 GCJ02 坐标系下的经纬度。 gcj02ToWgs84:将 GCJ02 坐标系下的经纬度转换为 WGS84 坐标系下的经纬度。 gcj02ToBd09:将 GCJ02 坐标系下的经纬度转换为 BD09 坐标系下的经纬度。 bd09ToGcj02:将 BD09 坐标系下的经纬度转换为 GCJ02 坐标系下的经纬度。
1153 0
84坐标系、02坐标系、百度坐标之间相互转换算法
|
7月前
|
图形学 计算机视觉
GEE错误——如何将原有矢量将维度转化为地理坐标系,重投影坐标坐标无法实现?
GEE错误——如何将原有矢量将维度转化为地理坐标系,重投影坐标坐标无法实现?
86 0
|
算法 JavaScript 数据可视化
基于leaflet-velocity的二维动态风场展示
本文讲解了leaflet-velocity插件,并利用插件进行了模拟的动态风场、洋流等信息的综合展示,让读者掌握集成方式。
1128 0
基于leaflet-velocity的二维动态风场展示
cesium中绘制立方体、设置材质、操作相机及获取鼠标经纬度和高度的方法
cesium中绘制立方体、设置材质、操作相机及获取鼠标经纬度和高度的方法
278 0
|
JSON 定位技术 API
百度地图高级开发:获取某范围半径圆形区域检索覆盖物内的所有标注的解决方案(1)
百度地图高级开发:获取某范围半径圆形区域检索覆盖物内的所有标注的解决方案(1)
301 0
下一篇
DataWorks