学习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>

效果如下:

相关推荐
踏实探索4 天前
Three.js教程_02场景、相机与渲染器全面解析
开发语言·javascript·数码相机·threejs
gis分享者6 天前
学习threejs,实现配合使用WebWorker
多线程·threejs·webworker
AllBlue6 天前
threejs相机辅助对象cameraHelper
threejs
gis分享者9 天前
学习threejs,使用VideoTexture实现视频Video更新纹理
threejs·视频贴图·videotexture
gis分享者10 天前
学习threejs,通过设置纹理属性来修改纹理贴图的位置和大小
threejs·纹理贴图·位置和大小
gis分享者14 天前
学习threejs,使用设置normalMap法向量贴图创建更加细致的凹凸和褶皱
threejs·normalmap·法向量贴图
gis分享者14 天前
学习threejs,使用CubeCamera相机创建反光效果
threejs·cubecamera·立方体相机·反光效果
gis分享者20 天前
学习threejs,通过SkinnedMesh来创建骨骼和蒙皮动画
threejs·蒙皮网格·skinnedmesh·骨骼和蒙皮动画
gis分享者21 天前
学习threejs,导入FBX格式骨骼绑定模型
threejs·骨骼动画·fbx