2020年8月11日 星期二

[3D地圖]開始Cesium.js

使用Vue-cli 3為例
專案裝好後

1.安裝Cesium.js
yarn add cesium

2.設定vue.config.js
因為Cesium會去讀取"/Assets"和"/Workers"資料夾的內容
而且CESIUM_BASE_URL這個全域變數一定要先定義
所以要先把node_modules裡面的檔案copy一份到public去
const webpack = require('webpack')
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const cesiumSource = './node_modules/cesium/Source'
module.exports = {
  configureWebpack: {
    plugins: [
      new CopyWebpackPlugin([{
        from: path.join(cesiumSource, '../Build/Cesium/Workers'),
        to: 'Workers'
      }]),
      new CopyWebpackPlugin([{
        from: path.join(cesiumSource, 'Assets'),
        to: 'Assets'
      }]),
      new webpack.DefinePlugin({
        CESIUM_BASE_URL: JSON.stringify('./')
      })
    ]
  }
}

3.把HelloWorld.vue改成這樣
<template>
  <div class="hello">
    <div id="cesiumContainer"></div>
  </div>
</template>

<script>
const Cesium = require('cesium') //無法使用import方法
import 'cesium/Build/Cesium/Widgets/widgets.css'
export default {
  mounted() {
    this.initCesium()
  },
  methods: {
    initCesium() {
      let viewer = new Cesium.Viewer("cesiumContainer");
    }
  }
}
</script>
<style scoped>
#cesiumContainer{
  width: 60vw;
  height: 60vh;
}
</style>

4.yarn run serve後,地球出現啦
至此步驟的github


5.設定相機飛向台灣
至此步驟的github
export default {
  data() {
    return {
      viewer: {},
      taiwanPosition: {
        latitude: 23.6978,
        longitude: 120.9605
      }
    }
  },
  mounted() {
    this.initCesium()
    this.flyToTaiwan()
  },
  methods: {
    initCesium() {
      this.viewer = new Cesium.Viewer("cesiumContainer");
    },
    flyToTaiwan() {
      let { latitude, longitude } = this.taiwanPosition
      let height = 1000000
      this.viewer.camera.flyTo({ destination: new Cesium.Cartesian3.fromDegrees(longitude, latitude, height) })
    }
  }
}
 

6.放一隻龍到地圖上
至官方範例下載tileset
https://github.com/CesiumGS/3d-tiles-samples
把tileset.json與b3dm檔放到public資料夾
(正常都會再起一個tile server,但本文為了示範,故先放在前端,用web server起)
  mounted() {
    this.initCesium()
    this.addDragon()
  },
  methods: {
    initCesium() {
      this.viewer = new Cesium.Viewer("cesiumContainer");
    },
    addDragon() {
      let myTileset = new Cesium.Cesium3DTileset({
          url : '/my-dragon/tileset.json'
      })
      let tileset = this.viewer.scene.primitives.add(myTileset);
      this.viewer.zoomTo(tileset);
    },

這樣龍就出現啦


另外,還有一些靜態資源需要被放入public資料夾,故再修改一下vue.config.js

  configureWebpack: {
    plugins: [
      new CopyWebpackPlugin([{
        from: path.join(cesiumSource, '../Build/Cesium/Workers'),
        to: 'Workers'
      }]),
      new CopyWebpackPlugin([{
        from: path.join(cesiumSource, 'ThirdParty/Workers'),
        to: 'ThirdParty/Workers'
      }]),
      new CopyWebpackPlugin([{
        from: path.join(cesiumSource, 'Widgets'),
        to: 'Widgets'
      }]),
      new CopyWebpackPlugin([{
        from: path.join(cesiumSource, 'Assets'),
        to: 'Assets'
      }]),
      new webpack.DefinePlugin({
        CESIUM_BASE_URL: JSON.stringify('./')
      })
    ]
  }


至此步驟的github


7.但發現靠太近了,所以需要調整zoomTo的程度
先把heading pitch 與 range通通都歸零
至此步驟的github
    addDragon() {
      let myTileset = new Cesium.Cesium3DTileset({
          url : '/my-dragon/tileset.json'
      })
      let tileset = this.viewer.scene.primitives.add(myTileset);
      let heading = 0
      let pitch = 0
      let range = 0
      this.viewer.zoomTo(tileset, new Cesium.HeadingPitchRange(heading, pitch, range));
    },


8.試著改變heading



9.試著改變pitch



9.試著改變range




10.由官方文件介紹可知
https://cesium.com/docs/cesiumjs-ref-doc/HeadingPitchRange.html

10.1.heading是偏航角,單位是弧度(0~2π)
2π大概是6.283....
上面gif示範的0.5大概是28°左右

10.2.pitch是俯仰角,單位是弧度(0~2π)
2π大概是6.283....
上面gif示範的0.5大概是28°左右

10.3.range是相機與目標中心點的距離,單位是公尺
上面gif示範大概是3000公尺可以看到全貌


11.若想在這條龍的中心點加一張圖片

import squirtleImage from '@/assets/squirtle.png'
export default {
....
  mounted() {
    this.initCesium()
    this.addDragon()
    this.addImageToDragonCenter()
  },
  methods: {
    initCesium() {
      this.viewer = new Cesium.Viewer("cesiumContainer");
    },
    addDragon() {
      let myTileset = new Cesium.Cesium3DTileset({
          url : '/my-dragon/tileset.json'
      })
      this.tileset = this.viewer.scene.primitives.add(myTileset);
      let heading = 0
      let pitch = 0
      let range = 0
      this.viewer.zoomTo(this.tileset, new Cesium.HeadingPitchRange(heading, pitch, range));
    },
    addImageToDragonCenter() {
      this.tileset.readyPromise.then(() => {
        let center = this.tileset.boundingSphere.center //龍的中心點
        let newBillboardCollection = new Cesium.BillboardCollection()
        let billboards = this.viewer.scene.primitives.add(newBillboardCollection)
        billboards.add({
          position: center,
          image: squirtleImage
        })
      })
    },


唉呀傑尼龜太大隻
至此步驟的github


12.修改一下長寬以後發現,他真得很中心耶
        billboards.add({
          position: center,
          image: squirtleImage,
          width : 50,
          height : 100,
        })


13.為了瞭解中心點以及怎麼調整,我印出了center的值
console.log(this.tileset.boundingSphere.center);
---------------------------------------------------------
Cartesian3
x: 1215107.7612304366
y: -4736682.902037748
z: 4081926.095098698


14.原來是一個Cartesian3(3D笛卡爾座標)
這是一個用x y z表示3D座標的概念
然後我發現官方文件有可以從經緯度轉成x y z的方法
https://cesium.com/docs/cesiumjs-ref-doc/Cartesian3.html
二話不說就把台北經緯度放進去
console.log(Cesium.Cartesian3.fromDegrees(121.5654, 25.0330));
---------------------------------------------------------
Cartesian3
x: -3026957.1459976556
y: 4926912.803821924
z: 2682387.0380952046


15.決定把傑尼龜從龍裡面拿出來

15.1先調整x軸
先調整視角
heading: 1.5
pitch: -1.5


喔原來x軸是這個方向
    addDragon() {
      let myTileset = new Cesium.Cesium3DTileset({
          url : '/my-dragon/tileset.json'
      })
      this.tileset = this.viewer.scene.primitives.add(myTileset);
      let heading = 1.5
      let pitch = -1.5
      let range = 0
      this.viewer.zoomTo(this.tileset, new Cesium.HeadingPitchRange(heading, pitch, range));
    },
    addImageToDragonCenter() {
      this.tileset.readyPromise.then(() => {
        let center = this.tileset.boundingSphere.center //龍的中心點
        let x = center.x + 500
        let y = center.y
        let z = center.z
        let newCenter = new Cesium.Cartesian3(x, y, z)
        let newBillboardCollection = new Cesium.BillboardCollection()
        let billboards = this.viewer.scene.primitives.add(newBillboardCollection)
        billboards.add({
          position: newCenter,
          image: squirtleImage,
          width : 50,
          height : 100,
        })
      })
    },

15.2調整y軸
先調整視角
heading: 1.2
pitch: -0.5

15.3調整z軸
先調整視角
heading: 1.2
pitch: 0


至此步驟的github



下一篇: [3D地圖]了解笛卡爾座標系

沒有留言:

張貼留言