当前位置: 首页 > article >正文

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

介绍

此项目使用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.kler.cn/a/307950.html

相关文章:

  • GPT-5 要来了:抢先了解其创新突破
  • Zookeeper的安装与使用
  • Linux screen和cscope工具使用总结
  • StructuredStreaming (一)
  • C++编程:利用环形缓冲区优化 TCP 发送流程,避免 Short Write 问题
  • JWT深度解析:Java Web中的安全传输与身份验证
  • 遗传算法及其MATLAB实现
  • Leetcode 字母异位词分组
  • Error: ENOENT: no such file or directory, uv_cwd
  • JVM-内存区域
  • 新装mysql8 并开启外网连接
  • 理解DataLoader
  • Redis——常用数据类型hash
  • 华为地图服务功能概览 -- HarmonyOS自学7
  • 【LeetCode Hot 100】169. 多数元素
  • Python快速入门 —— 第五节:接口开发
  • [项目][WebServer][ThreadPool]详细讲解
  • 猫狗识别大模型——基于python语言
  • C# WPF中实现深拷贝的五种方式
  • 商业银行零售业务数智运营探索与应用
  • BLE 协议之物理层
  • TCP核心机制
  • 数据结构(7.3_2)——平衡二叉树
  • iOS 18 适配 Xcode 16 问题
  • 线性代数(宋浩版)(4)
  • 基于Java、SpringBoot、Vue的加油站管理系统设计