学习threejs,加载天地图

👨‍⚕️ 主页: gis分享者

👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!

👨‍⚕️ 收录于专栏:threejs gis工程师


文章目录


一、🍀前言

本文详细介绍如何基于threejs在三维场景中加载天地图,亲测可用。希望能帮助到您。一起学习,加油!加油!

1.1 ☘️Web墨卡托投影

墨卡托(Mercator)投影,又名"等角正轴圆柱投影",荷兰地图学家墨卡托(Mercator)在1569年拟定,假设地球被围在一个中空的圆柱里,其赤道与圆柱相接触,然后再假想地球中心有一盏灯,把球面上的图形投影到圆柱体上,再把圆柱体展开,这就是一幅标准纬线为零度(即赤道)的"墨卡托投影"绘制出的世界地图。
Web墨卡托投影坐标系:

以整个世界范围,赤道作为标准纬线,本初子午线作为中央经线,两者交点为坐标原点,向东向北为正,向西向南为负。

X轴:由于赤道半径为6378137米,则赤道周长为2PI r = 2*20037508.3427892,因此X轴的取值范围:[-20037508.3427892,20037508.3427892]。

Y轴:[-20037508.3427892,20037508.3427892]之间。

二、🍀加载天地图

1. ☘️实现思路

  • 1、初始化renderer渲染器
  • 2、初始化Scene三维场景scene
  • 3、初始化camera相机,定义相机位置 ,设置相机方向lookAt。
  • 4、定义initListener方法,实现鼠标按下和抬起事件,更改鼠标样式。
  • 5、加载天地图:创建THREE.AxesHelper坐标辅助工具axes,scene场景中添加axes。定义LonLat2WebMercator方法,实现经纬度转WebMercator坐标。定义WebMercator2Tileimage方法,实现Web墨卡托转成tile上的像素坐标,返回像素坐标,以及tile编号,在所在tile上的偏移。定义LonLat2WebGL方法,实现经纬度到webgl坐标的转换。定义loadImageTile方法,实现单个切片的加载。定义loadMap方法,实现加载天地图。定义main方法,实现115.650846, 34.415643附近天地图的加载,调用main方法。具体代码参考下面代码样例。
  • 6、加入controls控制。

2. ☘️代码样例

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>learn65(加载天地图)</title>
    <script src="lib/threejs/91/three.js"></script>
    <script src="https://johnson2heng.github.io/three.js-demo/lib/js/controls/OrbitControls.js"></script>
    <style>
        html, body {
            height: 100%;
            width: 100%;
            padding: 0;
            margin: 0;
            overflow: hidden;
        }

        #main {
            background-color: #ddd;
        }
    </style>
</head>
<body>
<div id="main"></div>
</body>
<script>
  var div = document.getElementById("main");
  var width = window.innerWidth
    , height = window.innerHeight;

  div.style.width = width + "px";
  div.style.height = height + "px";

  var fov = 70
    , ratio = width / height;

  console.log("div", width, height);

  var scene, camera, cameraControls, renderer;

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(fov, ratio, 1, 1000);
  camera.position.x = 0;
  camera.position.y = -150;
  camera.position.z = 300;
  camera.lookAt(scene.position);

  renderer = new THREE.WebGLRenderer({
    antialias: true//抗锯齿
  });
  renderer.setSize(width, height);
  renderer.setClearColor(0x000000);

  cameraControls = new THREE.OrbitControls(camera, renderer.domElement);
  cameraControls.target.set(0, 0, 0);
  cameraControls.maxDistance = 400;
  cameraControls.minDistance = 10;
  cameraControls.update();

  div.appendChild(renderer.domElement);

  //创建坐标轴并加入到scene
  var axes = new THREE.AxisHelper(50);
  scene.add(axes);

  var render = function () {
    cameraControls.update();
    renderer.render(scene, camera);
    requestAnimationFrame(render);
  }

  render();
  initListener();

  function initListener() {
    var onmousedown = function (event) {
      div.style.cursor = "move";
      //div.addEventListener("mousemove",onmousemove);
    };

    var onmouseup = function (event) {
      div.style.cursor = "default";
      //div.removeEventListener("mousemove",onmousemove)
    };

    div.addEventListener("mousedown", onmousedown);
    div.addEventListener("mouseup", onmouseup);
  }

  /*******************************************************************/

  //切图在scene中的大小
  var tileSize = 50;
  //地图切片服务地址
  // var serverURL = "https://c.tile.osm.org/";
  // 天地图官网申请tk
  var mytk = '';
  //设置中心经纬度
  var centerLng = 0
    , centerLat = 0;

  //WGS84转Web墨卡托
  //参考:http://www.opengsc.com/archives/137
  function LonLat2WebMercator(lng, lat) {
    var x = (lng / 180.0) * 20037508.3427892;
    var y;
    if (lat > 85.05112) {
      lat = 85.05112;
    }
    if (lat < -85.05112) {
      lat = -85.05112;
    }
    y = (Math.PI / 180.0) * lat;
    var tmp = Math.PI / 4.0 + y / 2.0;
    y = 20037508.3427892 * Math.log(Math.tan(tmp)) / Math.PI;
    var result = {
      x: x,
      y: y
    };
    return result;
  }

  //Web墨卡托转成tile上的像素坐标,返回像素坐标,以及tile编号,在所在tile上的偏移
  function WebMercator2Tileimage(x, y) {
    //对于第18级地图, 对于我国而言
    var level = 18;
    var r = 20037508.3427892;
    y = r - y;
    x = r + x;
    var size = Math.pow(2, level) * 256;
    var imgx = x * size / (r * 2);
    var imgy = y * size / (r * 2);
    //当前位置在全球切片编号
    var col = Math.floor(imgx / 256);
    var row = Math.floor(imgy / 256);
    console.log("col", col, "row", row);
    //当前位置对应于tile图像中的位置
    var imgdx = imgx % 256;
    var imgdy = imgy % 256;

    //像素坐标
    var position = {
      x: imgx,
      y: imgy
    };
    //tile编号
    var tileinfo = {
      x: col,
      y: row,
      level: 18
    };
    //在所在tile上的偏移
    var offset = {
      x: imgdx,
      y: imgdy
    };

    var result = {
      position: position,
      tileinfo: tileinfo,
      offset: offset
    };
    return result;
  }

  //经纬度到tile,再到WebGL坐标
  function LonLat2WebGL(lng, lat) {
    var webMercator = LonLat2WebMercator(lng, lat);
    var tilePos = WebMercator2Tileimage(webMercator.x, webMercator.y).position;

    var centerWM = LonLat2WebMercator(centerLng, centerLat);
    // var centerWM = LonLat2WebMercator(lng, lat);
    var centerTP = WebMercator2Tileimage(centerWM.x, centerWM.y);
    //相对偏移修正(以centerLng,centerLat所在点tile中心点为原点,导致的偏移)
    var x = (tilePos.x - centerTP.position.x + (centerTP.offset.x - 256 / 2)) * tileSize / 256;
    var y = (tilePos.y - centerTP.position.y + (-centerTP.offset.y + 256 / 2)) * tileSize / 256;

    var result = {
      x: x,
      y: y
    };
    return result;
  }

  /**
   * 加载一个切图
   * @param {Object} xno tile编号x
   * @param {Object} yno tile编号y
   * @param {Object} callback
   */
  function loadImageTile(xno, yno, callback) {
    var level = 18;
    // var url = serverURL + level + "/" + xno + "/" + yno + ".png";
    var url = 'https://t1.tianditu.gov.cn/vec_w/wmts?tk=' + mytk + '&SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&TILEMATRIX=' + level + '&TILEROW=' + yno + '&TILECOL=' + xno + '&FORMAT=tiles'
    var loader = new THREE.TextureLoader();
    //跨域加载图片
    loader.crossOrigin = true;
    loader.load(url, function (texture) {
      console.log("loaded tile");
      var geometry = new THREE.PlaneGeometry(tileSize, tileSize, 1);
      var material = new THREE.MeshBasicMaterial({
        map: texture,
        transparent: true,
        side: THREE.DoubleSide//双面显示
      });
      var mesh = new THREE.Mesh(geometry, material);
      callback(mesh);
    });
  }

  /**
   * 将加载的切图放到scene
   * @param {Object} mesh
   * @param {Object} x坐标  WebGL坐标
   * @param {Object} y坐标
   */
  function addTileToScene(mesh, x, y) {
    //mesh的中心位置
    mesh.position.x = x;
    mesh.position.y = y;
    scene.add(mesh);
  }

  /**
   * 辅助函数,用于计算tile应该放在何处
   * @param {Object} dx  tile间相对位置,也就是编号差
   * @param {Object} dy
   */
  function addTileToSceneHelper(dx, dy) {
    var x = tileSize * dx;
    var y = -tileSize * dy;
    return function (mesh) {
      addTileToScene(mesh, x, y)
    };
  }

  /**
   * 加载地图
   * @param {Object} centerX 地图中间的切图编号
   * @param {Object} centerY 地图中间的切图编号
   */
  function loadMap(centerX, centerY) {
    var radius = 5;
    for (var i = centerX - radius; i <= centerX + radius; i++) {
      for (var j = centerY - radius; j <= centerY + radius; j++) {
        //console.log("try to load",i,j,i-centerX,j-centerY);
        console.log("try to load");
        loadImageTile(i, j, addTileToSceneHelper(i - centerX, j - centerY));
      }
    }
  }

  /**
   * 标记出当前位置
   * @param {Object} x webGL坐标
   * @param {Object} y
   */
  function markCurrentPosition(x, y) {
    var geometry = new THREE.SphereGeometry(10, 30, 30);
    var material = new THREE.MeshBasicMaterial({
      color: 0xff0000
    });
    var mesh = new THREE.Mesh(geometry, material);
    mesh.position.x = x;
    mesh.position.y = y;
    scene.add(mesh);
  }

  function main() {
    // navigator.geolocation.getCurrentPosition(function (position) {
    //   var lng = position.coords.longitude;
    //   var lat = position.coords.latitude;
    //   console.log("current position in world", lat, lng);
    //   centerLat = lat;
    //   centerLng = lng;
    //
    //   var webMercator = LonLat2WebMercator(lng, lat);
    //   var tilePos = WebMercator2Tileimage(webMercator.x, webMercator.y);
    //
    //   //以centerLng所在点tile中心点为中心,加载tile
    //   loadMap(tilePos.tileinfo.x, tilePos.tileinfo.y);
    //
    //   //标记当前位置
    //   var currentWebGLPos = LonLat2WebGL(lng, lat);
    //   markCurrentPosition(currentWebGLPos.x, currentWebGLPos.y);
    // });
    var webMercator = LonLat2WebMercator(115.650846, 34.415643);
    var tilePos = WebMercator2Tileimage(webMercator.x, webMercator.y);
    loadMap(tilePos.tileinfo.x, tilePos.tileinfo.y);
    // var currentWebGLPos = LonLat2WebGL(115.650846, 34.415643);
    // markCurrentPosition(currentWebGLPos.x, currentWebGLPos.y);
  }

  main();
</script>
</html>

效果如下:

相关推荐
gis分享者4 天前
学习threejs,导入assimp & assimp2json格式的模型
threejs·三维模型·assimp·assimp2json
gis分享者5 天前
学习threejs,导入AWD格式的模型
threejs·awd·three.awdloader
gis分享者11 天前
学习threejs,导入pdb格式的模型
threejs·pdb模型·three.pdbloader
gis分享者13 天前
学习threejs,THREE.CircleGeometry 二维平面圆形几何体
threejs·圆形几何体·circlegeometry
gis分享者13 天前
学习threejs,THREE.RingGeometry 二维平面圆环几何体
threejs·圆环几何体·ringgeometry
AllBlue14 天前
blender中合并的模型,在threejs中显示多个mesh;blender多材质烘培成一个材质
blender·threejs
gis分享者19 天前
学习threejs,PerspectiveCamera透视相机和OrthographicCamera正交相机对比
threejs·透视相机·正交相机
gis分享者22 天前
学习threejs,scene.overrideMaterial全局材质效果
threejs·全局材质·overridemater
我码玄黄24 天前
在THREEJS中加载3dtile模型
前端·javascript·3d·threejs