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

news2024/11/28 4:39:43

前言

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 的核心功能,大家可以去其官网​​​​​​​上详细体验一下,主打的是一个高颜值的三维地图引擎,当然除了好看之外,其他地图框架该有的功能它也具备,只是官网给人的感觉过于粗糙,不够吸引人;另外产品试用的门槛有些高,希望后面能优化吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1577052.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

spring面试八股

常用的注册bean的方式 ComponentScan扫描到的service和Controller等的注解 Configration配置类或者是xml文件的定义。 spring中有几种依赖注入的方式 1.构造器注入。 2.setter方法注入。 3.使用field属性的方式注入。 applicationContext是什么 spring bean spring aop Aop…

03-JAVA设计模式-建造者模式

建造者模式 什么是建造者模式 建造者模式&#xff08;Builder Pattern&#xff09;是一种对象构建的设计模式&#xff0c;它允许你通过一步一步地构建一个复杂对象&#xff0c;来隐藏复杂对象的创建细节。 这种模式将一个复杂对象的构建过程与其表示过程分离&#xff0c;使得…

目标点注意力Transformer:一种用于端到端自动驾驶的新型轨迹预测网络

目标点注意力Transformer&#xff1a;一种用于端到端自动驾驶的新型轨迹预测网络 附赠自动驾驶学习资料和量产经验&#xff1a;链接 摘要 本文介绍了目标点注意力Transformer&#xff1a;一种用于端到端自动驾驶的新型轨迹预测网络。在自动驾驶领域中&#xff0c;已经有很多…

深度比较Vue 3.0中的computed和watch属性用法与最佳实践

摘要&#xff1a;在Vue 3.0中&#xff0c;computed和watch属性是用于处理数据逻辑的重要工具。本文将详细对比这两个属性的工作原理、适用场景以及使用时的注意事项&#xff0c;旨在帮助开发者更有效地选择和使用它们。 一、computed属性 computed属性是Vue 3.0中用于计算数据…

wpf TreeView 实现动态加载页面

实现以下的效果&#xff0c;在TreeView上点击节点&#xff0c;动态加载右边的页面&#xff0c;如下图所示&#xff1a; 1. 主页面如下&#xff1a; 2. 实现主页面刷新方法 _currentStateViewModel.RefreshState(); _currentStateViewModel就是点击TreeView上的节点&#xff0…

Linux安装最新版Docker完整教程

参考官网地址&#xff1a;Install Docker Engine on CentOS | Docker Docs 一、安装前准备工作 1.1 查看服务器系统版本以及内核版本 cat /etc/redhat-release1.2 查看服务器内核版本 uname -r这里我们使用的是CentOS 7.6 系统&#xff0c;内核版本为3.10 1.3 安装依赖包 …

c++20协程详解(四)

前言 到这就是协程的最后一节了。希望能帮到大家 代码 到这里我们整合下之前二、三节的代码 #include <coroutine> #include <functional> #include <chrono> #include <iostream> #include <thread> #include <mutex> #include <me…

政安晨【AIGC实践】(一):在Kaggle上部署使用Stable Diffusion

目录 简述 开始 配置 执行 安装完毕&#xff0c;一键运行 结果展示 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 人工智能数字虚拟世界实践 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

spring加载类初始化顺序

今天看spring官网的时候&#xff0c;提到了Ordered执行顺序。我当时记得PostConstruct注解会在bean加载后执行&#xff0c;现在又来了一个执行顺序&#xff0c;直接给我整蒙了。 于是我写了一个简单的dom来看看&#xff0c;它是什么&#xff1a; Service("t2ServerImpl&q…

Vue - 你知道Vue中key的工作原理吗

难度级别:中级及以上 提问概率:80% 在Vue项目开发中,并不推荐使用索引做为key,以为key必须是唯一的,可以使用服务端下发的唯一ID值,也不推荐使用随机值做为key,因为如果每次渲染都监听到不一样的key,那么节点将无法复用,这与Vue节省…

中药提取物备案数据库<5000+中药提取物>

NMPA中药提取物备案数据库的建立是确保中药提取物质量安全、规范生产行为、加强监管、保障公众用药安全、促进产业发展和国际化的重要措施。 通过查询中药提取物备案信息我们能了解到中药提取物的实用备案号、药品通用名称、药品生产企业、批准文号、备案日期、备案状态、中药…

分表?分库?分库分表?实践详谈 ShardingSphere-JDBC

如果有不是很了解ShardingSphere的可以先看一下这个文章&#xff1a; 《ShardingSphere JDBC?Sharding JDBC&#xff1f;》基本小白脱坑问题 阿丹&#xff1a; 在很多开发场景下面&#xff0c;很多的技术难题都是出自于&#xff0c;大数据量级或者并发的场景下面的。这里就出…

【LeetCode刷题记录】11. 盛最多水的容器

11 盛最多水的容器 给定一个长度为 n 的整数数组 height。有 n 条垂线&#xff0c;第 i 条线的两个端点是 ( i , 0 ) (i, 0) (i,0)和 ( i , h e i g h t [ i ] ) (i, height[i]) (i,height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的…

MySQL学习路线一条龙

引言 在当前的IT行业&#xff0c;无论是校园招聘还是社会招聘&#xff0c;MySQL的重要性不言而喻。 面试过程中&#xff0c;MySQL相关的问题经常出现&#xff0c;这不仅因为它是最流行的关系型数据库之一&#xff0c;而且在日常的软件开发中&#xff0c;MySQL的应用广泛&#…

数字人直播系统是什么?AI数字人直播间搭建方法来了!

无人直播的时代&#xff0c;短视频和直播平台正在风口&#xff0c;各条赛道内也早已人满为患&#xff0c;很多线下商家都想参与其中&#xff0c;因为时间、地方、设备等限制久久不能去实行起来。所以&#xff0c;数字人直播新模式成为了线下商家的救星&#xff0c;线下商家方法…

unity_Button:单击的三种实现方式

此代码直接绑定到button上面无需其他操作 using UnityEngine; using UnityEngine.UI;public class PrintHelloOnButtonClick : MonoBehaviour {private Button button;void Start(){// 获取当前GameObject上的Button组件button GetComponent<Button>();// 添加点击事件…

探索基于WebRTC的有感录屏技术开发流程

title: 探索基于WebRTC的有感录屏技术开发流程 date: 2024/4/7 18:21:56 updated: 2024/4/7 18:21:56 tags: WebRTC录屏技术屏幕捕获有感录屏MediaStream实时传输音频录制 第一章&#xff1a;技术原理 WebRTC&#xff08;Web Real-Time Communication&#xff09;是一种开放源…

什么是FIG图片格式?如何把jpg图片转FIG格式?

一&#xff0c;什么是FIG图片格式 FIG图片格式&#xff0c;全称为Figma Image Format&#xff0c;是一种由Figma设计软件专用的图像格式。Figma是一款强大的在线协作设计工具&#xff0c;广泛应用于UI/UX设计、产品设计和图标设计等领域。 二&#xff0c;FIG图片格式的优点 …

Prometheus服务发现与监控案例-Day 02

1. 服务发现简介 prometheus采用pull方式拉取指定目标实例的监控数据&#xff0c;也就是间隔固定的周期去目标实例上抓取metrics数据&#xff0c;每一个被抓取的目标实例都需要暴露一个数据指标API接口&#xff0c;prometheus通过这个暴露的接口就可以获取到其指标数据. 这种方…

MySQL基础练习题:习题2-3

这部分主要是为了帮助大家回忆回忆MySQL的基本语法&#xff0c;数据库来自于MySQL的官方简化版&#xff0c;题目也是网上非常流行的35题。这些基础习题基本可以涵盖面试中需要现场写SQL的问题。上期帮助大家建立数据库&#xff0c;导入数据&#xff0c;接下来让我们继续练习。 …