【GIS开发小课堂】写一个高德地图巡航功能的小DEMO

news2024/9/20 9:40:02

介绍

此项目使用vite为基础架构,内部实现均以typescript开发,可替换为自己的业务逻辑,并迁移到reactvueumi等其他框架。

通过调用高德地图的API和threejs的开发,实现了一个小鸭子(可替换为自己的模型)沿着规划路线行走,并使镜头跟随小鸭子前进的功能。

该功能主要常见于外卖平台的骑手、滴滴司机、以及情侣软件的实时共享位置。

实现思路

1.加载地图

使用AMapLoader.load加载地图,从高德开放平台控制台申请一个属于自己的key
import AMapLoader from '@amap/amap-jsapi-loader';...
const AMap = await AMapLoader.load({  
"key": "您自己申请的KEY",   // 申请好的Web端开发者Key,首次调用 load 时必填    
"version": "2.0",    
"plugins": ["AMap.Walking", "AMap.Driving"],    // 需要使用的的插件列表,如比例尺'AMap.Scale'等    
"Loca": { 
       version: '2.0.0'   
   }
})

使用new AMap.Map实例化地图,并设置mapStyle"amap://styles/grey",也可以在官网上自己设计属于自己的风格,主要讲的不是这部分所以大概交代一下就过去了,实例化Map后返回一个map实例,后续的操作都需要用到。

添加GLCustomLayer图层
new AMap.GLCustomLayer({    zIndex: 100,    init:()=>{},    render: ()=>{}})

threejs的加载和创建需要在init属性的方法里操作,render主要是用来渲染一些镜头信息和 WebGLRenderer的重绘。

在init方法中创建一个THREEJS的透视相机。

camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 100, 1 << 30);

2.镜头信息的获取

前文实例化Map后获取一个map的实例,其中提供了customCoords数据转换的工具,可以从这里获取到镜头信息,后续转化经纬度到3D世界坐标时候也会用到,转换工具需要提前获取到,方便后续的工作。

var { near, far, fov, up, lookAt, position } = customCoords.getCameraParams();

转换工具提供一个getCameraParams方法,其中包含相机位置等其他属性。


fov — 摄像机视锥体垂直视野角度
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面


其中大部分属性都和threejs的透视相机属性相同,在render方法中更新相机,这样做的作用就是在后续做巡航功能时会实时更新相机位置。

camera.near = near;camera.far = far;camera.fov = fov;camera.position.set(...position);camera.up.set(...up);camera.lookAt(...lookAt);camera.updateProjectionMatrix();

3.初始化loca

可视化图层需要用到Loca容器,需要在地图外绘制的图层,需要在可视化图层上绘制。

创建可视化作品前,首先要创建一个 Loca 容器,这个容器可以帮您加载高德地图作为底图,或者帮您关联已有的高德地图作为底图。


在使用地图的时,您可以使用任何一个 JS API 已有的功能,Loca 容器不会影响原有地图的任何功能和特性。这里创建的 Loca 容器您可以理解为是可视化图层的管理器。

注意:创建地图时候 Loca 版本要和map的版本一致,否则会报错。​​​​​​​

var loca = new (window as any).Loca.Container({    map,    zIndex: 9});

将创建好的AMap.GLCustomLayer添加到map图层。​​​​​​​

    const customLayer = await createGLCustomLayer(AMap, customCoords)    map.add(customLayer);

createGLCustomLayer方法就是之前定义的初始化AMap.GLCustomLayer方法。

返回一个GLCustomLayer实例,这样就可以在地图内插入可视化内容。

加载模型

回到new AMap.GLCustomLayer提供的init属性中,创建一个3d场景并把模型加载到场景中。​​​​​​​

renderer = new THREE.WebGLRenderer({    context: gl,  // 地图的 gl 上下文});
// 自动清空画布这里必须设置为 false,否则地图底图将无法显示renderer.autoClear = false;
scene = new THREE.Scene();

加载模型方法跟threejs相同,使用gltfloderapi,加载方法返回一个promise,再使用。​​​​​​​​​​​​​​

// 初始化模型function initGltf(): Promise<THREE.Object3D> {    return new Promise((resolve, reject) => {        var loader = new GLTFLoader();        loader.load('https://a.amap.com/jsapi_demos/static/gltf/Duck.gltf', (gltf: any) => {            let object = gltf.scene;            resolve(object)        });    })}

模型添加到场景。​​​​​​​

const { x, y, z } = setRotation(new THREE.Vector3(90, 90, 0))
object.scale.set(15, 15, 15);

group.add(object)group.add(new THREE.AxesHelper(100))scene.add(group)object.name = 'duck'

图片

我在模型上添加了一个AxesHelper辅助线,官网上表示蓝色代表z轴,但是放在地图中发生了坐标方向不一致的问题,threejs的向上方向是y轴,地图中z是向上方向,这一点可能要注意一下了。

用于简单模拟3个坐标轴的对象。
红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴。

4.旋转模型​​​​​​​

const { x, y, z } = setRotation(new THREE.Vector3(90, -90, 0))group.rotation.set(x, y, z)

获取旋转角度的方法。​​​​​​​

export function setRotation(rotation: THREE.Vector3) {    var x = Math.PI / 180 * (rotation.x || 0);    var y = Math.PI / 180 * (rotation.y || 0);    var z = Math.PI / 180 * (rotation.z || 0);    return new THREE.Vector3(x, y, z)}

图片

5.计算轨迹

使用viewControl

现在模型已经加载好,接下来就是要获取轨迹数据,镜头跟踪在Loca中有相应的apiviewControl,使用这个api调用addTrackAnimate方法,提供对应参数即可。

loca.viewControl.addTrackAnimate({    path: pathArr, // 镜头轨迹,二维数组,支持海拔    duration: 120000, // 时长    timing: [[0, 0.3], [1, 0.7]], // 速率控制器    rotationSpeed: 1800, // 每秒旋转多少度}, function () {    console.log('完成',);});

pathArr是一个轨迹数组。

const pathArr = [[116.310348, 39.89702], [116.310541, 39.884855], [116.320963, 39.889154], [116.322894, 39.889608], [116.325542, 39.889822], [116.328074, 39.889761], [116.349104, 39.889429], [116.348517, 39.89747], [116.355205, 39.898413], [116.35656, 39.90021], [116.355802, 39.93225]]

为了方便查看,我们在使用Loca提供的绘制引导线功能将这几个关键点连接的引导线画一下。​​​​​​​

// 导航线var polyline = new AMap.Polyline({    path: pathArr,            // 设置线覆盖物路径    showDir: true,    strokeColor: '#3366bb',   // 线颜色    strokeWeight: 10,           // 线宽    zIndex: 1});map.add(polyline)

以上工作做完后,需要调用一下loca.animate.start();方法,否则可视化图层不会更新,相应数据也获取不到,现在画面变成这样。

图片

除了以上这种方法去实现镜头的移动,还可以通过插入坐标的方式去实现,也是传统threejs中使用的方法,就是利用tweenjs的动画,运动过程中改变map.setCenter,实现跟踪,这部分代码在changeObject方法中。

6.镜头跟踪

移动模型

利用requestAnimationFrame函数写一个循环渲染的方法,在调用的同时获取镜头中心坐标,通过customCoords转换工具将经纬度转为3D世界的坐标,并将该坐标赋值给object模型,再通过map提供的getRotation方法,获取地图的旋转角度,并将该角度赋值给object模型的y轴,使模型沿着y轴旋转,至于旋转的速度,在前面定义addTrackAnimate时的rotationSpeed属性定义的。​​​​​​​

const render = () => {    requestAnimationFrame(() => {        render()    })    if (object) {        const center = map.getCenter()        var position = customCoords.lngLatsToCoords([            [center.lng, center.lat]        ])[0];        const v3 = new THREE.Vector3(position[1], 0, position[0])        object.position.copy(v3)        const rotation = map.getRotation()
        object.rotation.y = rotation * Math.PI / 180    }    map.render();    TWEEN && TWEEN.update()}

以上文章内容有一些关于threejs的基础知识,可以先提前了解一下,否则有很多好玩有趣的效果实现不出来。

图片

7.其他

关于飞线,只是作为装饰,显得画面不那么呆板,在官网上也有案例,简单贴一个代码吧。

 // 飞线var geo = new (window as any).Loca.GeoJSONSource({    url: 'https://a.amap.com/Loca/static/loca-v2/demos/mock_data/bj_bus.json',});
var layer = new (window as any).Loca.PulseLineLayer({    // loca,    zIndex: 10,    opacity: 1,    visible: true,    zooms: [1, 30],});
var headColors = ['#EFBB51', '#7F3CFF', '#4CC19B', '#0B5D74', '#E06AC4', '#223F9B', '#F15C1A', '#7A0FA6'];
layer.setSource(geo);
layer.setStyle({    altitude: 0,    lineWidth: 2,    // 脉冲头颜色    headColor: (_, feature) => {        return headColors[feature.properties.type - 1];    },    // 脉冲尾颜色    trailColor: 'rgba(128, 128, 128, 0.5)',    // 脉冲长度,0.25 表示一段脉冲占整条路的 1/4    interval: 0.25,    // 脉冲线的速度,几秒钟跑完整段路,可以通过计算距离动态改变时间    duration: 5000,});// 飞线结束
loca.add(layer);

three.js的版权声明及许可证:​​​​​​​

The MIT License
Copyright © 2010-2024 three.js authors
Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included inall copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE.

tween.js的版权声明及许可证:​​​​​​​

The MIT License
Copyright (c) 2010-2012 Tween.js authors.
Easing equations Copyright (c) 2001 Robert Penner http://robertpenner.com/easing/
Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included inall copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS INTHE SOFTWARE.

相关源码和模型的下载链接地址

https://www.aspiringcode.com/content?id=17086628146313&uid=a8012550ef80420fa58669250592734c

本文由高德开放平台用户—孙华鹏提供

仅代表作者个人观点

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

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

相关文章

如何使用UWA Gears连接模拟器进行性能测试

UWA Gears 是UWA最新发布的无SDK性能分析工具。针对移动平台&#xff0c;提供了实时监测和截帧分析功能&#xff0c;帮助您精准定位性能热点&#xff0c;提升应用的整体表现。 日常工作中&#xff0c;模拟器是测试岗位常用的一款工具&#xff0c;能够很好地解决例如公司内无法…

产业园区数字化转型升级怎么做?这个应对策略你或许可以参考下!

近年在政策红利、技术创新、需求升级等多重因素驱动下&#xff0c;中国产业园区数字化转型步伐加快&#xff0c;呈现出四个发展新趋势&#xff1a; 空间载体向虚实交互的数字空间拓展 服务模式向产业链级生态化服务升级 赋能工具向依托产业大脑“协同作战”演进 发展方式向注…

基于鸿蒙API10的RTSP播放器(五:拖动底部视频滑轨实现跳转)

拖动前播放位置&#xff1a; 拖动后播放位置&#xff1a; 在Slider组件中&#xff0c;添加onChange方法进行监听&#xff0c;当视频轨道拖放结束时&#xff0c;触发this.seekTo()函数&#xff0c;其中seekTo函数需要传递一个视频已播放时长作为参数 Slider({ value: this.p…

每日一练:两两交换链表中的节点

24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 一、题目要求 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff…

【网络】传输层协议UDP

一、再谈端口号 1.1 理解端口号 网络中两台主机进行通信的本质是主机的进程间进行通信&#xff0c;端口号标识了主机进行通信的不同的应用程序。 在 TCP/IP 协议中, 用 "源 IP", "源端口号", "目的 IP", "目的端口号", "协议号…

浅谈新型电力系统背景下虚拟电厂运营体系研究-安科瑞叶西平

摘要&#xff1a;随着智能电网关键技术以及自动需求响应技术的日益发展&#xff0c;分布式发电、储能、电动汽车、可控负荷等需求侧资源有望成为发电侧可调资源的有效替代资源&#xff0c;通过响应电力市场中的电价信号或政府和能源行业的政策激励参与需求响应项目的实施&#…

java重点学习-集合(List)

七 集合&#xff08;List&#xff09; 7.1 复杂度分析 7.2 数组 1.数组(Array)是一种用连续的内存空间存储相同数据类型 数据的线性数据结构。 2.数组下标为什么从0开始 寻址公式是:baseAddressi*dataTypeSize&#xff0c;计算下标的内存地址效率较高 3.查找的时间复杂度 随机(…

如何把提醒事项以倒数日的形式放在桌面上?

在快节奏的现代生活中&#xff0c;我们常常需要记住各种重要的日期和事件&#xff0c;比如会议、纪念日、项目截止日期等。如果能将这些提醒事项以倒计时的形式直接展示在桌面上&#xff0c;无疑会为我们的生活和工作带来极大的便利。这样的功能不仅可以帮助我们提前做好规划&a…

oracle 使用 PL/SQL Developer创建表并插入单条、多条数据

第一步&#xff1a;使用工具创建表&#xff08;前提是库已经创建好了&#xff09;&#xff1a;在当前用户下找到Tables 然后点击并右键&#xff0c;点击新建 写上表名&#xff0c;写上表名的注释 第二步添加字段&#xff1a;点击列&#xff0c;然后分别写上你自己需要的字段及名…

“跨越数据边界:企业级实时计算平台构想”——2024 DolphinDB 年度峰会演讲回顾

9 月 6 日&#xff0c;“以实时&#xff0c;见未来”2024 DolphinDB 年度峰会在杭州举办。DolphinDB 创始人、CEO 周小华博士为大家带来了主题为“跨越数据边界&#xff1a;企业级实时计算平台构想”的精彩演讲。 从最初的一站式大数据平台&#xff0c;到高性能时序数据库&…

个性化推送太多?OFGB帮你关闭,隐私无忧

随着win 11的推出&#xff0c;微软在提供新功能和改进的同时&#xff0c;也在系统中加入了各种形式的广告&#xff0c;这些广告虽然为微软带来了额外的收入&#xff0c;但却可能影响了我们的体验&#xff0c;甚至引起了一些不满。 在这样的背景下&#xff0c;开发者社区中出现…

第15-05章:获取运行时类的完整结构

我的后端学习大纲 我的Java学习大纲 6.1.第一组方法API: 1.API列表&#xff1a;java.lang.Class 类&#xff1a; 2.代码测试&#xff1a; public class ReflectionUtils{ puvblic static void main(String[] args){}// 第一组Testpublic void api_01{//上面截图的代码......…

MVVM 基础

文章目录 MVC 设计模式传统的 MVC 架构Cocoa version of MVCMVC 设计模式的几个指导原则 MVVM 设计模式IOS 上的应用鸿蒙上 MVVMAndroid Data binding 参考&#xff1a; MVC 设计模式 日常大家都会听到&#xff0c;名字很简单&#xff0c;但是很实用&#xff0c;日常 Android …

紧急警报 工厂水泵房深夜意外进水:物联网监控如何避免百万损失

紧急警报&#xff01;工厂水泵房深夜意外进水&#xff1a;物联网监控如何助力避免百万损失 在当今高度自动化的工业生产环境中&#xff0c;任何微小的异常都可能迅速升级为重大的安全事故&#xff0c;对企业的运营造成不可估量的影响。近期&#xff0c;一起发生在某大型制造工…

数据挖掘实战-基于朴素贝叶斯算法构建真假新闻分类模型

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

外包干了半年,快要废了。。。

先说一下自己的情况&#xff0c;普通本科&#xff0c;在外包干了半年多的功能测试&#xff0c;这几年因为大环境不好&#xff0c;我整个人心惊胆战的&#xff0c;怕自己卷铺盖走人了&#xff0c;我感觉自己不能够在这样蹉跎下去了&#xff0c;长时间呆在一个舒适的环境真的会让…

10个登录页面优化的案例研究和示例,激发你的灵感

什么是落地页优化&#xff0c;为什么它很重要&#xff1f; 落地页优化是一门将首次浏览转化为行动&#xff08;即转化率&#xff09;的艺术和科学。其主要目的是调整和微调您的数字欢迎界面&#xff0c;让每一位访问者都感受到这是为他们量身打造的。 这个过程不仅仅是简单的…

电脑监控软件系统有哪些?|2024年干货整理,赶紧码住!

在一家知名互联网公司里&#xff0c;由于缺乏有效的监管措施&#xff0c;一些员工利用工作时间进行与工作无关的活动&#xff0c;如网购、观看视频、玩游戏等小技怠工的情况&#xff0c;这种现象被称为“摸鱼”。 尽管表面上看似无害&#xff0c;但长期下来却严重拖累了整个团队…

Chisel简明教程

1. 简介 Chisel&#xff08;Scala嵌入式硬件构造语言&#xff09;是一种嵌入在高级编程语言Scala中的硬件构造语言。Chisel是一个特殊类定义、预定义对象和Scala内部使用约定的库&#xff0c;因此当你编写Chisel代码时&#xff0c;实际上是在编写一个构建硬件图的Scala程序。随…

也谈PCIe带宽问题

每每谈到PCIe速率的时候&#xff0c;必不可少要谈论的就是PCIe的带宽问题。互联网上也有很多帖子在谈这个事情&#xff0c;但总觉得差那么点意思。我从几个维度来讨论我的理解&#xff0c;希望有些信息能带给大家不一样的看法。先贴张PCIe速率提升图。 理论带宽、实际带宽、带宽…