THREE + d3制作中国地图挤压(extrude)模型

3 篇文章 0 订阅
2 篇文章 0 订阅

下午花了几个小时边学边做,做出来了一个中国地图的挤压模型。其中中国地图的数据是geojson的格式,由于相关法律这里无法提供地图数据。如果想要学习交流使用可以前往github上翻一翻。

使用的工具很单纯:

  • THREE.js (ver 11.5,主要用了挤压模型和缓冲模型,材质使用的基础半透明材质和线材质)
  • d3.v5.js (只用到了d3-geo的Mercator变换和其他的坐标变换小工具)

参考:

效果如下图:

全部代码先懒洋洋地堆在这儿,什么时候想起来了就解释一下↓

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>map-3d</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      body {
        overflow: hidden;
      }
    </style>
  </head>
  <body>
    <canvas id="main-canvas"></canvas>

    <script src="../../../javascripts/three115.min.js"></script>
    <script src="../../../javascripts/OrbitControls.js"></script>
    <script src="../../../javascripts/d3.v5.min.js"></script>
    <script>
      let main_canvas;
      let scene, camera, renderer;
      let map;
      function init(geo_data) {
        console.log(geo_data);
        main_canvas = document.querySelector("#main-canvas");
        main_canvas.setAttribute("width", window.innerWidth);
        main_canvas.setAttribute("height", window.innerHeight);

        scene = new THREE.Scene();
        scene.background = new THREE.Color("#01111A");

        camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight);
        camera.position.set(0, 0, 80);

        renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true, canvas: main_canvas });

        // map
        let map = new THREE.Object3D();
        const projection = d3.geoMercator().center([104.0, 37.5]).scale(80).translate([0, 0]);
        geo_data["features"].forEach((e) => {
          const province = new THREE.Object3D();
          const coors = e["geometry"]["coordinates"];
          coors.forEach((multipolygon) => {
            multipolygon.forEach((polygon) => {
              const shape = new THREE.Shape();
              const lineMaterial = new THREE.LineBasicMaterial({ color: "#FFF", linewidth: 1 });
              const lineGeometry = new THREE.BufferGeometry();
              let line_vertices = [];
              for (let i = 0; i < polygon.length; i++) {
                const [x, y] = projection(polygon[i]);
                if (i === 0) shape.moveTo(x, -y);
                shape.lineTo(x, -y);
                line_vertices.push(new THREE.Vector3(x, -y, 4.01));
              }
              lineGeometry.setFromPoints(line_vertices);
              const extrudeSettings = {
                depth: 4,
                bevelEnabled: false,
              };
              const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
              const material = new THREE.MeshBasicMaterial({ color: "#069EF2", transparent: true, opacity: 0.6 });
              const mesh = new THREE.Mesh(geometry, material);
              province.add(mesh);
              const line = new THREE.Line(lineGeometry, lineMaterial);
              province.add(line);
            });
          });

          province.properties = e.properties;
          if (e.properties.centroid) {
            const [x, y] = projection(e.properties.centroid);
            province.properties._centroid = [x, y];
          }
          map.add(province);
        });
        map.rotation.x = ((-Math.PI / 2) * 3) / 4;
        scene.add(map);

        controls = new THREE.OrbitControls(camera, renderer.domElement);
        controls.minDistance = 1;
        controls.maxDistance = 5000;
        controls.maxPolarAngle = Math.PI * 2;
        controls.minPolarAngle = 0;
        controls.enabled = true;
      }
      function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
      }
      function startup() {
        fetch("../../../data/geodata/china_2.json")
          .then((res) => res.json())
          .then((data) => {
            init(data);
            animate();
          });
      }
      startup();
    </script>
  </body>
</html>

 

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值