Mars3D与第三方集成

1. 引言

Mars3D是基于Cesium的Web端的三维GIS库,对Cesium做了进一步封装和扩展

Mars3D官网:Mars3D三维可视化平台 | 火星科技

Mars3D开发手册:开发教程 - Mars3D三维可视化平台 | 火星科技

GitHub地址:Mars3D三维可视化平台 | 火星科技

API文档:API文档 - Mars3D三维可视化平台 | 火星科技

以下是一些Mars3D与一些第三方库集成的使用案例

2. 集成示例

2.1 Turf

Turf客户端分析库,【需要引入Turf库】

将数据转换为GeoJson对象并使用Turf进行分析

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!--引入cesium基础lib-->
    <link href="https://unpkg.com/mars3d-cesium/Build/Cesium/Widgets/widgets.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/mars3d-cesium/Build/Cesium/Cesium.js" type="text/javascript"></script>
    <!--引入turf基础lib-->
    <script src="http://mars3d.cn/lib/turf/turf.min.js"></script>
    <!--引入mars3d库lib-->
    <link href="https://unpkg.com/mars3d/dist/mars3d.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/mars3d/dist/mars3d.js" type="text/javascript"></script>

    <style>
        html,
        body,
        .mars3d-container {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <div id="mars3dContainer" class="mars3d-container"></div>
    <script>
        let mapOptions = {
            basemaps: [{ name: "天地图", type: "tdt", layer: "img_d", show: true }],
        }
        const map = new mars3d.Map("mars3dContainer", mapOptions)
        map.setSceneOptions({
            center: { lat: 31.72076, lng: 117.033888, alt: 223798, heading: 0, pitch: -45 }
        })

        // 创建矢量数据图层
        const geoJsonLayer = new mars3d.layer.GeoJsonLayer({
            url: "https://openlayers.org/en/latest/examples/data/geojson/roads-seoul.geojson",
            symbol: {
                type: "polyline",
                styleOptions: {
                    color: "#f00",
                    width: 4
                }
            },
            flyTo: true
        })
        map.addLayer(geoJsonLayer)

        //绑定监听事件
        geoJsonLayer.on(mars3d.EventType.load, function (event) {
            console.log('矢量数据对象加载完成', event)
            const geojson = geoJsonLayer.toGeoJSON()
            console.log(geojson)
            const buffered = turf.buffer(geojson, 25, { units: 'meters' })
            console.log(buffered)
            const bufferedLayer = new mars3d.layer.GeoJsonLayer({
                data: buffered,
                symbol: {
                    type: "polygon",
                    styleOptions: {
                        color: "#0ff"
                    }
                },
                flyTo: true
            })
            map.addLayer(bufferedLayer)
        })

    </script>

</body>

</html>

image-20230207231621086

2.2 MapV

  • new mars3d.layer.MapVLayer(options, dataSet)

MapV图层 【需要引入 mapv.js 库 和 mars3d-mapv 插件库】

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!--引入cesium基础lib-->
    <link href="https://unpkg.com/mars3d-cesium/Build/Cesium/Widgets/widgets.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/mars3d-cesium/Build/Cesium/Cesium.js" type="text/javascript"></script>
    <!--引入mapV基础lib-->
    <script src="http://mars3d.cn/lib/mapV/mapv.min.js"></script>
    <!--引入mars3d库lib-->
    <link href="https://unpkg.com/mars3d/dist/mars3d.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/mars3d/dist/mars3d.js" type="text/javascript"></script>
    <script src="http://mars3d.cn/lib/mars3d/plugins/mapv/mars3d-mapv.js"></script>
    <style>
        html,
        body,
        .mars3d-container {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <div id="mars3dContainer" class="mars3d-container"></div>
    <script>
        let mapOptions = {
            basemaps: [{ name: "天地图", type: "tdt", layer: "img_d", show: true }],
        }
        const map = new mars3d.Map("mars3dContainer", mapOptions)
        map.setSceneOptions({
            center: { lat: 31.72076, lng: 117.033888, alt: 223798, heading: 0, pitch: -45 }
        })

        // 构造数据
        let positions = []
        let geojson = []
        let randomCount = 300
        while (randomCount--) {
            // 取区域内的随机点
            let point = [random(113 * 1000, 119 * 1000) / 1000, random(28 * 1000, 35 * 1000) / 1000]
            positions.push(Cesium.Cartesian3.fromDegrees(point[0], point[1]))

            geojson.push({
                geometry: {
                    type: "Point",
                    coordinates: point
                },
                count: 30 * Math.random()
            })
        }
        console.log(geojson)

        map.camera.flyTo({
            destination: Cesium.Rectangle.fromCartesianArray(positions)
        })

        // mapv图层参数
        let options = {
            fillStyle: "rgba(55, 50, 250, 0.8)",
            shadowColor: "rgba(255, 250, 50, 1)",
            shadowBlur: 20,
            max: 100,
            size: 50,
            label: {
                show: true,
                fillStyle: "white"
            },
            globalAlpha: 0.5,
            gradient: {
                0.25: "rgb(0,0,255)",
                0.55: "rgb(0,255,0)",
                0.85: "yellow",
                1.0: "rgb(255,0,0)"
            },
            draw: "honeycomb",
            data: geojson // 数据
        }

        // 创建MapV图层
        const mapVLayer = new mars3d.layer.MapVLayer(options)
        map.addLayer(mapVLayer)

        mapVLayer.on("click", function (event) {
            console.log("单击了图层", event)
        })

        function random(min, max) {
            return Math.floor(Math.random() * (max - min + 1) + min)
        }

    </script>

</body>

</html>

image-20230207232659612

2.3 Echarts

  • new mars3d.layer.EchartsLayer(options)

Echarts图层, 【需要引入 echarts 库 和 mars3d-echarts 插件库】

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!--引入cesium基础lib-->
    <link href="https://unpkg.com/mars3d-cesium/Build/Cesium/Widgets/widgets.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/mars3d-cesium/Build/Cesium/Cesium.js" type="text/javascript"></script>
    <!--引入echarts基础lib-->
    <script src="http://mars3d.cn/lib/echarts/echarts.min.js"></script>
    <script src="http://mars3d.cn/lib/echarts/echarts-gl/echarts-gl.min.js"></script>
    <!--引入mars3d库lib-->
    <link href="https://unpkg.com/mars3d/dist/mars3d.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/mars3d/dist/mars3d.js" type="text/javascript"></script>
    <script src="http://mars3d.cn/lib/mars3d/plugins/echarts/mars3d-echarts.js"></script>
    <style>
        html,
        body,
        .mars3d-container {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <div id="mars3dContainer" class="mars3d-container"></div>
    <script>
        let mapOptions = {
            basemaps: [{ name: "天地图", type: "tdt", layer: "img_d", show: true }],
        }
        const map = new mars3d.Map("mars3dContainer", mapOptions)
        map.setSceneOptions({
            center: { lat: 30.589203, lng: 120.732051, alt: 18446, heading: 2, pitch: -49 }
        })

        mars3d.Util.fetchJson({ url: "//data.mars3d.cn/file/apidemo/lineroad.json" })
            .then(function (json) {
                let options = {
                    animation: false,

                    visualMap: {
                        type: "piecewise",
                        left: "right",
                        bottom: 46,
                        min: 0,
                        max: 15,
                        splitNumber: 5,
                        maxOpen: true,
                        color: ["red", "yellow", "green"],
                        textStyle: {
                            color: "#ffffff"
                        }
                    },
                    tooltip: {
                        formatter: function (params, ticket, callback) {
                            return "拥堵指数:" + params.value
                        },
                        trigger: "item"
                    },
                    series: [
                        {
                            type: "lines",
                            coordinateSystem: "mars3dMap",
                            polyline: true,
                            data: json.data,
                            lineStyle: {
                                normal: {
                                    opacity: 1,
                                    width: 4
                                },
                                emphasis: {
                                    width: 6
                                }
                            },
                            effect: {
                                show: true,
                                symbolSize: 2,
                                color: "white"
                            }
                        }
                    ]
                }

                const echartsLayer = new mars3d.layer.EchartsLayer(options)
                map.addLayer(echartsLayer)
            })
    </script>

</body>

</html>

image-20230208101504970

2.4 ThreeJS

ThreeJS集成,这里使用了官方示例的集成代码,【需要引入Three JS库和集成代码】

集成代码如下:

const BaseLayer = mars3d.layer.BaseLayer
const THREE = window.THREE

// 与THREE.js集成
class ThreeLayer extends BaseLayer {
  constructor(options = {}) {
    super(options)

    this._pointerEvents = this.options.pointerEvents
  }

  _showHook(show) {
    if (show) {
      this._threejsContainer.style.visibility = "visible"
    } else {
      this._threejsContainer.style.visibility = "hidden"
    }
  }

  /**
   * 对象添加到地图前创建一些对象的钩子方法,
   * 只会调用一次
   * @return {void}  无
   * @private
   */
  _mountedHook() {
    if (!THREE) {
      throw new Error("请引入 three.js 库 ")
    }

    const scene = this._map.scene

    const threeContainer = mars3d.DomUtil.create("div", "mars3d-threejs")
    threeContainer.style.position = "absolute"
    threeContainer.style.top = "0px"
    threeContainer.style.left = "0px"
    threeContainer.style.width = scene.canvas.clientWidth + "px"
    threeContainer.style.height = scene.canvas.clientHeight + "px"
    threeContainer.style.pointerEvents = this._pointerEvents ? "auto" : "none" // auto时可以交互,但是没法放大地球, none 没法交互
    this._container = threeContainer

    const fov = 45
    const aspect = scene.canvas.clientWidth / scene.canvas.clientHeight
    const near = 1
    const far = 10 * 1000 * 1000 // needs to be far to support Cesium's world-scale rendering

    this.scene = new THREE.Scene()
    this.camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
    this.renderer = new THREE.WebGLRenderer({ alpha: true })
    threeContainer.appendChild(this.renderer.domElement)
  }

  /**
   * 对象添加到地图上的创建钩子方法,
   * 每次add时都会调用
   * @return {void}  无
   * @private
   */
  _addedHook() {
    if (this._container) {
      this._map.container.appendChild(this._container)
    }

    this._map.viewer.useDefaultRenderLoop = false // 关闭自动渲染

    // eslint-disable-next-line
    const that = this
    ;(function frame() {
      // animateFrame: requestAnimationFrame事件句柄,用来清除操作
      that._animateFrame = window.requestAnimationFrame(frame)
      that.update() // 按帧率执行
    })()
  }

  /**
   * 对象从地图上移除的创建钩子方法,
   * 每次remove时都会调用
   * @return {void}  无
   * @private
   */
  _removedHook() {
    window.cancelAnimationFrame(this._animateFrame)
    delete this._animateFrame

    this._map.viewer.useDefaultRenderLoop = true

    if (this._container) {
      this._map.container.removeChild(this._container)
    }
  }

  update() {
    this.renderCesium()
    this.renderThreeObj()
    this.renderCamera()
  }

  renderCesium() {
    this._map.viewer.render()
  }

  renderThreeObj() {
    const width = this._container.clientWidth
    const height = this._container.clientHeight
    this.renderer.setSize(width, height)
    this.renderer.render(this.scene, this.camera)
  }

  renderCamera() {
    // register Three.js scene with Cesium
    this.camera.fov = Cesium.Math.toDegrees(this._map.camera.frustum.fovy) // ThreeJS FOV is vertical
    this.camera.updateProjectionMatrix()

    // Clone Cesium Camera projection position so the
    // Three.js Object will appear to be at the same place as above the Cesium Globe

    this.camera.matrixAutoUpdate = false

    this.camera.lookAt(new THREE.Vector3(0, 0, 0))

    const cvm = this._map.camera.viewMatrix
    const civm = this._map.camera.inverseViewMatrix

    this.camera.matrixWorld.set(
      civm[0],
      civm[4],
      civm[8],
      civm[12],
      civm[1],
      civm[5],
      civm[9],
      civm[13],
      civm[2],
      civm[6],
      civm[10],
      civm[14],
      civm[3],
      civm[7],
      civm[11],
      civm[15]
    )

    this.camera.matrixWorldInverse.set(
      cvm[0],
      cvm[4],
      cvm[8],
      cvm[12],
      cvm[1],
      cvm[5],
      cvm[9],
      cvm[13],
      cvm[2],
      cvm[6],
      cvm[10],
      cvm[14],
      cvm[3],
      cvm[7],
      cvm[11],
      cvm[15]
    )

    const width = this._map.scene.canvas.clientWidth
    const height = this._map.scene.canvas.clientHeight
    this.camera.aspect = width / height
    this.renderer.setSize(width, height)
    this.camera.updateProjectionMatrix()

    this.renderer.clear()
    this.renderer.render(this.scene, this.camera)
  }
}

示例代码文件:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!--引入cesium基础lib-->
    <link href="https://unpkg.com/mars3d-cesium/Build/Cesium/Widgets/widgets.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/mars3d-cesium/Build/Cesium/Cesium.js" type="text/javascript"></script>
    <!--引入threejs基础lib-->
    <script src="http://mars3d.cn/lib/three/three.js"></script>
    <!--引入mars3d库lib-->
    <link href="https://unpkg.com/mars3d/dist/mars3d.css" rel="stylesheet" type="text/css" />
    <script src="https://unpkg.com/mars3d/dist/mars3d.js" type="text/javascript"></script>
    <script src="http://mars3d.cn/example/thirdParty/threejs/demo/ThreeLayer.js"></script>
    <style>
        html,
        body,
        .mars3d-container {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <div id="mars3dContainer" class="mars3d-container"></div>
    <script>
        let mapOptions = {
            basemaps: [{ name: "天地图", type: "tdt", layer: "img_d", show: true }],
        }
        const map = new mars3d.Map("mars3dContainer", mapOptions)
        map.setSceneOptions({
            center: { lat: 30.980053, lng: 117.375049, alt: 110976, heading: 357, pitch: -50 }
        })

        const threeLayer = new ThreeLayer()
        map.addLayer(threeLayer)

        let minWGS84 = [117.142184, 31.869697]
        let maxWGS84 = [117.357015, 31.713898]
        let ce = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2 - 1, 200000)

        let geometry = new THREE.SphereGeometry(1, 32, 32)
        const sphere = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({ color: 0xffffff, side: THREE.DoubleSide })) // 12面体

        // translate "up" in Three.js space so the "bottom" of the mesh is the handle
        sphere.scale.set(5000, 5000, 5000)
        sphere.uuid = "sphere"
        const sphereYup = new THREE.Group()
        sphereYup.add(sphere)
        threeLayer.scene.add(sphereYup) // don’t forget to add it to the Three.js scene manually
        sphereYup.position.set(ce.x, ce.y, ce.z)

        let arrXdObj = []

        let xdObj = new XDObject()
        xdObj.threeMesh = sphereYup
        xdObj.minWGS84 = minWGS84
        xdObj.maxWGS84 = maxWGS84
        arrXdObj.push(xdObj)

        geometry = new THREE.DodecahedronGeometry()
        const dodecahedronMesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial()) // 12面体
        dodecahedronMesh.scale.set(5000, 5000, 5000)
        dodecahedronMesh.position.z += 15000
        // translate "up" in Three.js space so the "bottom" of the mesh is the handle
        dodecahedronMesh.rotation.x = Math.PI / 2 // rotate mesh for Cesium's Y-up system
        dodecahedronMesh.uuid = "12面体"

        const dodecahedronMeshYup = new THREE.Group()
        dodecahedronMeshYup.add(dodecahedronMesh)
        threeLayer.scene.add(dodecahedronMeshYup) // don’t forget to add it to the Three.js scene manually
        dodecahedronMeshYup.position.set(ce.x, ce.y, ce.z)

        // Assign Three.js object mesh to our object array
        xdObj = new XDObject()
        xdObj.threeMesh = dodecahedronMeshYup
        xdObj.minWGS84 = minWGS84
        xdObj.maxWGS84 = maxWGS84
        arrXdObj.push(xdObj)

        // 添加灯光,点光源
        const spotLight = new THREE.SpotLight(0xffffff)
        spotLight.position.set(0, 0, 50000)
        spotLight.castShadow = true // 设置光源投射阴影
        spotLight.intensity = 1
        sphereYup.add(spotLight)

        // 添加环境光
        const hemiLight = new THREE.HemisphereLight(0xff0000, 0xff0000, 1)
        sphereYup.add(hemiLight)

        let cartToVec = function (cart) {
            return new THREE.Vector3(cart.x, cart.y, cart.z)
        }

        // Configure Three.js meshes to stand against globe center position up direction
        for (var id in arrXdObj) {
            minWGS84 = arrXdObj[id].minWGS84
            maxWGS84 = arrXdObj[id].maxWGS84
            // convert lat/long center position to Cartesian3
            let center = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2)

            // get forward direction for orienting model
            let centerHigh = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2, 1)

            // use direction from bottom left to top left as up-vector
            let bottomLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1]))
            let topLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1]))
            let latDir = new THREE.Vector3().subVectors(bottomLeft, topLeft).normalize()

            // configure entity position and orientation
            arrXdObj[id].threeMesh.position.copy(center)
            arrXdObj[id].threeMesh.lookAt(centerHigh)
            arrXdObj[id].threeMesh.up.copy(latDir)
        }

        function XDObject() {
            this.threeMesh = null
            this.minWGS84 = null
            this.maxWGS84 = null
        }
    </script>

</body>

</html>

image-20230208103002385

3. 参考资料

[1]功能示例(Vue版) - Mars3D三维可视化平台 | 火星科技

[2]API文档 - Mars3D三维可视化平台 | 火星科技

[3]开发教程 - Mars3D三维可视化平台 | 火星科技

posted @ 2023-02-08 23:27  当时明月在曾照彩云归  阅读(1080)  评论(0编辑  收藏  举报