原来科技感的三维地图可以这么简单实现

前言

2024.02.20

下午摸鱼时接到一个客户的数字孪生项目的需求,客户什么也没说,就要求“炫酷”和“科技感”地图,还要把他们的模型都放上去,起初我以为又是一个可视化大屏的项目,准备用高德地图应付过去,然后他们又在群里发什么要求高之类的,我们的数据种类多,说什么高德、百度、Mapbox、Cesium之类框架都试过了,满足不了需求,好嘛,这下给我犯了难了,会的技术栈全都给我排除了, 手撸Three.js源码我可不干,于是就在网上晃悠,尝试找一些灵感 

2024.02.24

又试了几个地图框架,还是不行,被客户和经理催了一顿,再不行他们要换人了

2024.02.28 

 在QQ群里闲逛,发现了群友发的一个叫 Mapmost SDK for WebGL 的地图框架,于是抱着试一试的态度做了一下,好家伙,一下就对客户味了

2024.03.04

后面我发现这个SDK蛮有意思,于是把我实现客户需求的过程记录下来,分享给大家

 初始化

 这个SDK看似是个商用软件,不过目前是免费试用,官网上申请一下就行了,然后按照他们的文档,申请SDK授权码,填一下参数,就能初始化一个地图,和一般的地图SDK用法差不多

    <script src ='https://delivery.mapmost.com/cdn/sdk/webgl/v3.5.0/mapmost-webgl-min.js'></script>
    <script>
        let mapmost = window.mapmost
         /* 
        * 初始化地图
        */
        let map = new mapmost.Map({
            container: 'map', //地图容器
            style:'http://192.168.126.44/mms-style/darkMap.json', //矢量底图
            center: [120.74014004382997, 31.32975410974069], //地图中心点
            bearing: 50.399999999999636, //方位
            pitch: 78.99999999999993, //倾斜角
            zoom: 19.964625761228117, //缩放
            userId: '***',//授权码

        })
    </script>

不过,在此之前,要把底图中的矢量建筑图层隐藏掉,客户要加载真正的建筑三维模型。

代码和效果图如下:

const buildingLayers = [
    'buildings-di',
    'buildings-faguang-1',
    'buildings-faguang-2',
    'buildings-faguang-3',
    'buildings-high',
    'buildings-high-top',
    'buildings-low',
    'buildings-low-top',
    'buildings-lowmid',
    'buildings-lowmid-top',
    'buildings-mid',
    'buildings-mid-top',
    'buildings-midhigh',
    'buildings-midhigh-copy',
]

map.on('load', (e) => {
    buildingLayers.forEach((layer, index) => {
        let layerObj = map.getLayer(layer)
        map.setLayoutProperty(layerObj.id, 'visibility', 'none');
    })
})

 

加载建筑三维模型 

 这里我们准备了城市建筑的模型,格式为glb,按照文档描述,我们添加一个idmodelLayer的三维图层,代码和效果图如下

//...

/* 
* 加城市f载建筑模型
*/

let Group = null
let Layer = null

let models = ["./model/YQ.glb"].map(item => ({
    type: 'glb',
    url: item
}));

map.on('load',(e) => {
     let modelOptions = {
        id: 'modelLayer',
        type: 'model',
        models: models,
        sky: "./sky/satara_night_no_lamps_2k.hdr",
        exposure: 2.4,
        center: [120.74155610348487, 31.328251735532746, 0],
        callback:  (group, layer) => {
            Group = group
            Layer = layer                                    
        }
    };
    
    map.addLayer(modelOptions);       
})

 ​​​​​​​

添加三维效果 

 接下来就是客户的G点了,为三维场景添加特效:

 添加建筑流光渐变效果

 参考SDK的接口文档,给建筑加上流光渐变的效果

 

 定义一个添加特效的函数addModelEffect,然后按照文档上的参数说明来配置相关属性

 const  addModelEffect = () =>{
    Layer.addModelEffect(Group, [{
        type: "gradient",
        startColor: "rgba(63, 177, 245,.5)",
        endColor: "rgba(58, 142, 255,.8)",
        opacity: 0.8,
        percent: 0.5
    }, {
        type: "flow",
        speed: 1,
        color: "rgba(241, 99, 114, .4)",
        opacity: 0.8,
        percent: 0.05
    }])
    Group.addFrame(0x3FB1F5);

  }

然后我们在模型加载完成后调用这个函数:


//...

map.on('load',(e) => {
     let modelOptions = {
        id: 'modelLayer',
        type: 'model',
        models: models,
        sky: "./sky/satara_night_no_lamps_2k.hdr",
        exposure: 2.4,
        center: [120.74155610348487, 31.328251735532746, 0],
        callback:  (group, layer) => {
            Group = group
            Layer = layer   
            addModelEffect()
        }
    };
    
    map.addLayer(modelOptions);       
})

效果如下:

​​​​​​​

添加粒子飞线 

同样,在SDK的文档上找到添加流动线的接口,定义一个addFlowLine的函数,然后按照要求配置参数:

这里我们借助了一个生成贝塞尔曲线的函数,以及一些随机的坐标点位数据。 他们的作用是为我们提供必要的模拟数据

import { getBSRPoints } from './bezierFunction.js'
import { flowLineData } from './flowLineData.js'

//...

const addFlowLine = () => {
    //生成贝塞尔曲线测试数据
    let data_trail1 = getBSRPoints(120.71541557869517, 31.316503949907542,
        120.73787560336916, 31.321925190347713, 800);
    let data_trail2 = getBSRPoints(120.71541557869517, 31.316503949907542,
        120.72619950480242, 31.33360076088249, 1500);
    let data_trail3 = getBSRPoints(120.71541557869517, 31.316503949907542,
        120.69933418653403, 31.332725809914024, 900);
        
    [data_trail1, data_trail2, data_trail3].map(data => {
        Layer.addFlowLine({
            type: "trail",
            color: '#1ffff8',
            speed: 4,
            opacity: 0.9,
            width: 8,
            data: {
                coordinate: data
            }
        });
    })

    flowLineData.map(data => {
        Layer.addFlowLine({
            type: "flow",
            color: '#ff680d',
            speed: 4,
            opacity: 1,
            percent: 0.08,
            gradient: 0.02,
            width: 5,
            data: {
                coordinate: data
            }
        });
    })
}

同样,我们在模型加载完成后调用这个函数:


//...

map.on('load',(e) => {
     let modelOptions = {
        id: 'modelLayer',
        type: 'model',
        models: models,
        sky: "./sky/satara_night_no_lamps_2k.hdr",
        exposure: 2.4,
        center: [120.74155610348487, 31.328251735532746, 0],
        callback:  (group, layer) => {
            Group = group
            Layer = layer   
            addModelEffect()
            addFlowLine()
        }
    };
    
    map.addLayer(modelOptions);       
})

效果图如下:

 添加特效球

 类似的,文档上的添加特效球的接口,给场景里添加两个”能量半球“

 代码和效果图如下:


const addSphere = () => {
    let sphere = Layer.addSphere({
        color: "rgb(53, 108, 222)",
        radius: 3300, //半径,单位米,默认为10米
        segment: 256, //构成圆的面片数
        phiLength: Math.PI,
        speed: 3,
        opacity: 1,
        center: [120.67727020663829, 31.31997024841401, 0.0]
    });

    let sphere2 = Layer.addSphere({
        color: "rgb(219, 74, 51)",
        radius: 2300, //半径,单位米,默认为10米
        segment: 256, //构成圆的面片数
        phiLength: Math.PI,
        speed: 6,
        opacity: 1,
        center: [120.67727020663829, 31.31997024841401, 0.0]
    });
}


//...

map.on('load',(e) => {
     let modelOptions = {
        id: 'modelLayer',
        type: 'model',
        models: models,
        sky: "./sky/satara_night_no_lamps_2k.hdr",
        exposure: 2.4,
        center: [120.74155610348487, 31.328251735532746, 0],
        callback:  (group, layer) => {
            Group = group
            Layer = layer   
            addModelEffect()
            addFlowLine()
            addSphere()
        }
    };
    map.addLayer(modelOptions);       
})

环境效果调优 

仔细看整个环境,发现天是白的,和整体环境不搭配

更改一下地图初始化时的参数,将天空设置为暗色:

let map = new mapmost.Map({
    container: 'map', //地图容器
    style: 'http://192.168.126.44/mms-style/darkMap.json', //矢量底图
    center: [120.74014004382997, 31.32975410974069], //地图中心点
    bearing: 60.399999999999636, //方位
    pitch: 78.99999999999993, //倾斜角
    zoom: 14.964625761228117, //缩放
    sky: 'dark' //天空颜色
})

然后整体效果如下:

如果觉得场景本身太亮,可以降低添加模型时的曝光度:


let modelOptions = {
    exposure: .4,
    callback:  (group, layer) => {
       //...
    }
};

这样整体环境就会偏暗一点,更有黑夜下的赛博朋克城市的味道

当然,在这我又换了一张更暗的底图:

 最后,再调整一下特效球的半径和位置,行了,这就是客户喜欢的样子,哈哈哈,2小时搞定,而且不用手撸Three.js代码:

  const addSphere = () => {
    let sphere = Layer.addSphere({
        color: "rgb(53, 108, 222)",
        radius: 3300, //半径,单位米,默认为10米
        segment: 256, //构成圆的面片数
        phiLength: 180,
        speed: 3,
        opacity: 1,
        center: [120.67943361712065, 31.306450929918768]
    });

    let sphere2 = Layer.addSphere({
        color: "rgb(219, 74, 51)",
        radius: 2300, //半径,单位米,默认为10米
        segment: 256, //构成圆的面片数
        phiLength: 180,
        speed: 6,
        opacity: 1,
        center: [120.67727020663829, 31.31997024841401, 0.0]
    });
}

 

总结 

仔细看,不难发现,这个SDK集成了 Mapbox 和 Three.js 的核心功能,大家可以去其官网​​​​​​​上详细体验一下,主打的是一个高颜值的三维地图引擎,当然除了好看之外,其他地图框架该有的功能它也具备,只是官网给人的感觉过于粗糙,不够吸引人;另外产品试用的门槛有些高,希望后面能优化吧

  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在Vue中实现离线三维地图,可以使用一些开源的JavaScript库来帮助实现。下面是一个基本的步骤: 1. 选择一个合适的三维地图库,比如Cesium.js或者Three.js。Cesium.js是一个功能强大的地理信息系统(GIS)库,而Three.js是一个用于创建和渲染三维图形的库。根据你的需求选择其中之一。 2. 在Vue项目中引入所选库。你可以使用npm安装库,然后在Vue组件中导入它们。比如,如果选择了Cesium.js,可以使用以下命令安装: ``` npm install cesium ``` 然后,在Vue组件中导入Cesium.js: ```javascript import * as Cesium from 'cesium' ``` 3. 创建一个地图容器。在Vue组件的模板中添加一个容器元素,用于显示地图。比如: ```html <template> <div id="mapContainer"></div> </template> ``` 4. 在Vue组件的`mounted`生命周期钩子中初始化地图。在组件加载完成后,可以使用所选库的API来初始化地图并将其绑定到容器元素上。以下是Cesium.js的示例代码: ```javascript mounted() { const viewer = new Cesium.Viewer('mapContainer'); } ``` 这将在id为`mapContainer`的元素上创建一个新的Cesium地图。 5. 根据需要添加离线地图数据。离线地图数据可以是地形数据、影像数据或矢量数据。你可以通过将这些数据下载到本地,并在初始化地图时指定路径来添加离线数据。具体的数据格式和加载方法取决于所选的库和数据源。 注意:在使用任何地图库时,请确保遵守相关的许可协议和使用条款。 这只是一个基本的示例,具体的实现方法可能因所选的库和需求而有所不同。你可以根据自己的需要进一步探索和定制地图的功能和样式。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值