Cesium使用DEM地形数据(.tif)创建自定义Primitive

 DEM数据源:地理空间数据云 GDEMV3 30M 分辨率数字高程数据

第一步:用python把tif文件解析成高程数据,生成一个js文件,里面是高程数组,长度3601*3601

from osgeo import gdal, osr
import numpy as np

fname = 'ASTGTMV003_N30E104_dem';
url = './'+fname+'.tif'
ds = gdal.Open(url)
gt = ds.GetGeoTransform()
print(gt)

dem = ds.ReadAsArray()
print(dem.max())

f = open(fname+'.js', "a")

f.write('var dataDem = [')
for item in dem:
    for item2 in item:
        f.write(str(item2)+',')

f.write(']')
f.close()

第二步:把地形数据分成小块创建geomotry(一个3601*3601顶点的模型浏览器会崩溃,所以要切成小块)。

贴图使用mapbox的web墨卡托影像图,uv计算涉及到web墨卡托与经纬度的相互转换。

影像图的级别的选择:按照当前地形块的四个角在同一张影像图、且级别最大为原则。

源码地址:Apps/dem_tif_2.html · changjiuxiong/Cesium-1.62Test - Gitee.com

完整代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- Use correct character set. -->
  <meta charset="utf-8">
  <!-- Tell IE to use the latest, best version. -->
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
  <title>Hello World!</title>
  <script src="../Build/Cesium/Cesium.js"></script>
  <script src="./file/test.js"></script>


  <style>
    @import url(../Build/Cesium/Widgets/widgets.css);
    html, body, #cesiumContainer {
      width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
    }
  </style>
</head>
<body>
<div id="cesiumContainer">
</div>
<script>
    Cesium.Ion.defaultAccessToken='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJmNjJjMzY0OS1hZGQxLTRiZmYtYWYwNS03NmIyM2MwMDgwZDAiLCJpZCI6MTIzMTgsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1NjA4NDQ3Mjd9.OLTL_rs2gAi2R9zoztBHcJPDHnVl2Q7OZxRtZhoCeZE';
    let viewer = new Cesium.Viewer("cesiumContainer");

    let data = dataDem;
    let dataMinL = 86;
    let dataMinB = 27;
    let dataMaxL = 87;
    let dataMaxB = 28;

    let maxLength = 20037508.3427892;
    let maxLength2 = maxLength*2;

    let destination = Cesium.Cartesian3.fromDegrees(dataMinL, dataMaxB, 10000);

    let cutWidth = 36;
    let cutWidthL = cutWidth/3600;
    let cutWidthB = cutWidth/3600;
    for(let startY=0; startY<cutWidth*30; startY+=cutWidth){
      for(let startX=0; startX<cutWidth*30; startX+=cutWidth){

        let posList = [];
        let indexList = [];
        let uvList = [];
        let nList = [];

        let ld = [dataMinL+startX/3600, dataMaxB-(startY+cutWidth)/3600];
        let lu = [dataMinL+startX/3600, dataMaxB-startY/3600];
        let rd = [dataMinL+(startX+cutWidth)/3600, dataMaxB-(startY+cutWidth)/3600];
        let ru = [dataMinL+(startX+cutWidth)/3600, dataMaxB-startY/3600];
        let findXY;
        let z1 = 18;
        for(;z1>=0;z1--){
          let ldxy = getXYbyBLZ(ld[0],ld[1],z1);
          let luxy = getXYbyBLZ(lu[0],lu[1],z1);
          let rdxy = getXYbyBLZ(rd[0],rd[1],z1);
          let ruxy = getXYbyBLZ(ru[0],ru[1],z1);

          if(ldxy[0]===luxy[0]&&ldxy[0]===rdxy[0]&&ldxy[0]===ruxy[0] && ldxy[1]===luxy[1]&&ldxy[1]===rdxy[1]&&ldxy[1]===ruxy[1]){
            findXY = ldxy;
            break;
          }
        }
        let url1 = 'https://api.mapbox.com/v4/mapbox.satellite/'+z1+'/'+findXY[0]+'/'+findXY[1]+'.webp?sku=101iGH34Pnrym&access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4M29iazA2Z2gycXA4N2pmbDZmangifQ.-g_vE53SD2WrJ6tFX7QHmA';
        // console.log(url1);

        let count1 = Math.pow(2,z1);
        let ceil1 = maxLength2/count1;

        let minMx = findXY[0]*ceil1 - maxLength;
        let maxMx = minMx + ceil1;

        let maxMy = maxLength - (findXY[1]*ceil1);
        let minMy = maxMy - ceil1;

        let minLB = webMercator2LonLat(minMx, minMy);
        let maxLB = webMercator2LonLat(maxMx, maxMy);

        let lLen = maxLB[0] - minLB[0];
        let bLen = maxLB[1] - minLB[1];

        for(let y=startY; y<=startY+cutWidth; y++){
          for(let x=startX; x<=startX+cutWidth; x++){
            let height = data[y*3601+x];
            let B = dataMaxB - y/3600;
            let L = dataMinL + x/3600;

            let s = (L - minLB[0])/lLen;
            let t = (B - minLB[1])/bLen;
            uvList.push(s,t);

            let v3 = Cesium.Cartesian3.fromDegrees(L, B, height);
            posList.push(v3.x, v3.y, v3.z);

            nList.push(v3.x, v3.y, v3.z);
          }
        }

        let count = cutWidth + 1;
        for (let i = 0; i < count*count - count; i++) {
          if (i % count === count - 1) {
            continue;
          }

          indexList.push(i + 1);
          indexList.push(i);
          indexList.push(i + count);

          indexList.push(i + 1 + count);
          indexList.push(i + 1);
          indexList.push(i + count);
        }

        let pos = new Float64Array(posList);
        let normal = new Float32Array(nList);
        let index = new Uint32Array(indexList);
        let st = new Float32Array(uvList);
        let boundingSphere = Cesium.BoundingSphere.fromVertices(pos);

        //创建geometry
        let geometry = new Cesium.Geometry({
          attributes: {
            position: new Cesium.GeometryAttribute({
              componentDatatype: Cesium.ComponentDatatype.DOUBLE,
              componentsPerAttribute: 3,
              values: pos
            }),

            st: new Cesium.GeometryAttribute({
              componentDatatype: Cesium.ComponentDatatype.FLOAT,
              componentsPerAttribute: 2,
              values: st
            }),

            normal: new Cesium.GeometryAttribute({
              componentDatatype: Cesium.ComponentDatatype.FLOAT,
              componentsPerAttribute: 3,
              values: normal
            }),

          },
          indices: index,
          primitiveType: Cesium.PrimitiveType.TRIANGLES,
          boundingSphere: boundingSphere
        });

        //绘制三角带
        let myInstance = new Cesium.GeometryInstance({
          geometry: geometry,
          // geometry: Cesium.GeometryPipeline.toWireframe(geometry),
        });

        let primitive = new Cesium.Primitive({
          geometryInstances: myInstance,
          appearance: new Cesium.MaterialAppearance({
            material: Cesium.Material.fromType('Image', {
              image: url1,
            }),
            flat: false,
            faceForward: false,
          }),
        });

        viewer.scene.primitives.add(primitive);

      }
    }

    viewer.camera.flyTo({
      destination: destination,
      duration:1,
    });

    //经纬度转Web墨卡托
    function lonLat2WebMercator(l, b) {
      let x = l * 20037508.34/180;
      let y = Math.log(Math.tan((90+b)*Math.PI/360))/(Math.PI/180);
      y = y *20037508.34/180;
      return [x,y];
    }
    //Web墨卡托转经纬度
    function webMercator2LonLat(x, y) {
      let l = x/20037508.34*180;
      let b = y/20037508.34*180;
      b = 180/Math.PI*(2*Math.atan(Math.exp(b*Math.PI/180))-Math.PI/2);
      return [l,b];
    }

    function getXYbyBLZ(l,b,z0){

      let count0 = Math.pow(2,z0);
      let BL = [l, b];
      let ceil = maxLength2/count0;

      let me = lonLat2WebMercator(BL[0],BL[1]);
      let mex = me[0];
      let mey = me[1];

      let x = Math.floor((mex+maxLength)/ceil);
      let y = Math.floor((maxLength2-(mey+maxLength))/ceil);

      return [x,y];
    }

</script>
</body>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值