一、背景

地图空间可视化作为高德智慧交通前端业务中最重要的功能之一,承担着城市交通大脑、全境智能大屏等业务中大量的地图渲染需求。作为向用户展示交通数据的窗口,我们需要展现省、市、区、商圈、自定义区域多种场景,包括所有交通事件、拥堵指数、辖区等多种维度的数据,呈现着数据量大、元素种类多、逻辑展现重等特点。

JSAPI作为高德地图前端战线的引擎,涵盖着渲染地图、展示覆盖物等底层能力,但对于行业应用领域的开发来说,存在着开发难度大、适配成本高、纯原生JS实现与主流框架结合不紧密,无行业图层能力的问题。

基于以上原因,我们设计了具有适用于垂直行业的、可复用、可扩展、二次开发简单等特点的地图SDK,已经成为智慧交通地图空间可视化能力的首选方案。

二、方案设计

整体框架设计方案

高德智慧交通团队经过大量项目实践和思考,以交通行业为切入点,面向整个前端行业地图设计了一套地图空间可视化开发的SDK,整体功能架构设计如下图所示:

(1) MapContainer是整个SDK的基座,用于承载地图引擎,装载在其上渲染的覆盖物图层,加载所需要的框架模块,在整个架构中起到中流砥柱的作用。

(2) 配置控制器负责传入用户配置,包括地图应用key配置、加载可选功能配置、样式配置等,在用户变更这些配置后,它会把更新后的配置信息传递到流程中的其他模块中。

(3) 接受数据的工作由SourceLoader完成,设计了一套SDK内部使用的标准化的数据格式,Loader负责将用户传入的不同类型的数据(已经支持GeoJSON、WKT、数据列表等形式)转化成专用标准格式数据,分发到地图容器及各图层中。

(4) 为了支持不同的主流应用框架,将框架适配层单独拆分,由它将主要模块封装成Vue、React等框架兼容的组件形式,实现多框架扩展。

(5) 地图API调用有着严格的顺序限制,而封装框架对于图层各个生命周期的触发是异步的,乱序的,存在无法保证流程一致性的问题,为了应对这种情况我们在SDK中引入了事件队列机制。

状态驱动方案的实现

1.生命周期设计

地图JS API的调用逻辑与原生Javascript一样,是命令式调用设计,像下面这样:

// 创建地图
const map = new AMap.Map(options);// 添加覆盖物
const marker = new AMap.Marker(markerOptions);
map.add(marker);// 修改覆盖物属性
marker.setContext(newContext);// 移除覆盖物
map.remove(marker);
map.destroy();

这样的API调用方式,与上层项目的开发框架,如Vue、React等不匹配,如果在一个状态驱动的框架下充斥着大量命令式驱动的代码,会大幅度降低这个项目的可维护性、可扩展性。

为了更好地支撑开发的需要,所有业务图层抽象出了一套完整的生命周期流程。不同的图层,渲染逻辑的步骤不完全相同,各图层的额外能力,如支持交互事件的能力、动画能力也不尽相同,但都可以囊括在这一套生命周期结构内。

SDK图层组件生命周期定义如下:

(1)地图注册

在地图底座加载完毕后,会通知各个图层的RegisterMap流程,这是图层组件生命周期的第一步,图层中包含的所有元素都在这之后才会开始渲染。

(2)中间层加载

部分类型的元素需要分组批量加载,因此在渲染这些元素之前,需要先将对应的组图层加载出来。因此,我们设计了组图层相关的生命周期,相关逻辑只需要在beforeAppendGroup,appendGroup,afterAppendGroup这些流程中实现即可。

(3)元素加载

beforeAppendComponent,appendComponent,afterAppendComponent,这些是元素图层中最重要的流程,用于实现图层元素加载的主逻辑。

其中,对于一些元素需要有前置检查,有数据校验,可以把相应的检查逻辑放入beforeAppend中;有的元素需要注册交互事件,或者需要有添加动画scheme能力,这部分的实现逻辑可以放到afterAppend流程中。

与之对应的,还有元素的销毁流程,beforeRemoveComponent,removeComponent,afterRemoveComponent。如果元素绑定了交互事件,将会在beforeRemove的时候解绑;如果元素注册了动画或者周期调用,也会在beforeRemove的时候销毁周期timer。

(4)元素更新

shoudlUpdate,diff,updateComponent,用于实现组件数据动态更新后图层元素的diff、更新过程。

其中为了防止源数据中只有一小部分修改导致整个图层全部重绘的情况,我们在其中加入了diff的算法,通过各图层的校验数据key的方法,筛选出变更前后一致的数据项,只重绘不同的数据,大大提升了渲染流程的效率。

2.插件的实现

不同图层之间存在共同处理流程和共同的属性,对此,我们设计了各种可复用的内置插件,供各图层根据自身特性组合使用。

例如,有实现定时刷新效果的scheme插件,实现动画效果的animate插件,实现注册交互事件的event插件等。这些插件的设计,必须遵守组件生命周期的规范,插件功能的实现逻辑,也全部以注册上述的生命周期函数的方式完成。

这些生命周期需要与主流框架的生命周期设计适配。以目前我们项目中正在使用的Vue框架举例,Vue也有其自己的组件生命周期,它的设计基本能够与我们的周期函数相匹配。因此,针对Vue的适配过程其实并不怎么难:

Vue自身有一套不同层级的组件之间的加载控制流程,父子层级、兄弟层级之间的组件有着严格的触发顺序。例如,父组件的beforeCreate总是在子组件beforeCreate之前触发,而父组件的mounted又总是在子组件mounted之后才会响应,这与我们的多层级图层之间想要的触发顺序相符。因此,SDK图层的各生命周期总能在Vue中找到与之对应的触发时间点。

经过封装后,用SDK实现的地图模块在项目中生成的组件树结构如下:

3.异步流程的一致性设计

我们使用的底层地图引擎,对于流程逻辑的顺序有着严格的要求:

(1)地图底座的创建须在所有其他流程之前。

(2)Loca、L7底座的初始化须在地图底座创建完成之后。

(3)地图元素需要在地图底座加载完成后才能够开始加载,销毁也需要在地图底座销毁之前完成。

(4)需要确保状态与结果的一致性,如果在短时间内触发了大量的更新数据的操作,即使底层引擎处理需要很长的时间,也要保证最终的展示结果与更新的顺序完全一致。

不幸的是,虽然主流的框架有完善的生命周期管理机制,能够确保各个流程的执行顺序不出差错,但这些流程之间都是异步的、并发的,而绘图引擎在处理这些渲染指令时,会由于处理时长的不确定性,导致各指令返回的顺序有所变化,这可能会导致下面的情况出现:

  • 地图容器的加载时间过长,导致加载后续元素时,地图仍没有渲染完成而出错;

  • 在短时间内对同一份数据进行变更,如果引擎处理第一次变更花的时间比后一次更长,就会导致第一次更新的结果渲染出来时,会把更早完成的第二次渲染结果覆盖掉。

为了避免上述情况,我们在SDK中实现了事件队列控制器,处理顺序问题:

(1)所有图层组件中需要调用底座引擎的事件,例如append component,remove component等,不会直接调用底座的相关接口,而是在队列控制器中push一个对应类型的事件。

(2)队列控制器中的所有事件类型,全部封装成同步方法实现。由控制器收集所有涂层的调用消息,单线程逐一消费。

(3)在控制器中写入特殊的控制逻辑,地图基座的加载需要在其他图层加载之前,则把基座加载的事件的响应优先级设置为最高。

(4)引入筛选机制,针对队列中存在同一图层的互逆操作,如短时间内加载一份数据,之后又remove掉,由于这一对操作不会对当前的结果有任何影响,因此这一对操作将会被过滤逻辑删除,达到优化渲染性能的效果。

4.地图控制指令的优化

地图底座支持用户通过调用相关方法控制地图展示的视野,SDK在这种设计上加以优化,通过在地图底座组件上配置相应的属性状态,来实现定位到选定元素、定位到整个辖区范围、定位到特定地点及缩放级别等多种视野类型。

同时,地图的其他控制方法,例如设置周边避让区域、设置光标形状、设置自定义地图样式等方法,也全部改为传递props属性的方式实现。

三、其他优化

地图实例缓存

就我们使用的底层地图引擎来说,创建、销毁一个地图底座需要消耗大量的性能,而有时候这样的操作是可以避免的。有时候我们只是切换了一个页面路由,图面的上展示物并不需要有什么变化,但仍然会触发地图底座的销毁与重新生成。这个流程是多余的。

为了优化这个问题,我们设计了可以容纳2个底座实例的缓存容器。每次在执行销毁地图的命令时,我们并不会真正的销毁它,而是把它隐藏掉并存入缓存中。下次需要创建实例时,直接在缓存中找到符合要求的实例拿来用。

多实例环境隔离

随着下游业务项目的功能迭代,产品提出了在同一个页面内展示多个SDK底座实例的要求。对此,我们对SDK进行了一系列的优化:

  • 改造消息队列控制器,原来的单线程模式已经不再适用,现在已可以支持实例隔离,不同实例之间独享事件队列和流程控制逻辑。

  • 优化图层与底座的从属判定机制,在多个底座之间存在父子关系的情况下,能够让图层在最合适的底座上展现。

GL渲染Context没有正确GC回收导致的崩溃问题

在为L7编写加载器时,遇到了内存泄露的问题:如果在项目中使用了L7相关图层,销毁时L7使用的WebGLRenderingContext资源不会正确释放,反复创建销毁几次后,浏览器会因为内部的renderingContext资源不足而渲染崩溃。

分析L7源码后发现,L7为了实现与地图同步resize,在地图容器DOM上注册了一个resize事件,并把这个事件的处理函数绑定在了这个容器DOM的一个叫__resize__trigger__的属性上。

如果开发者在项目中使用Vue作为前端框架,Vue的模板更新机制会引起DOM的重绘,在一次数据变更之后,它会把原来的容器DOM销毁,替换为一个新的。

但由于注册的事件函数中含有DOM对象引用的缘故,虽然旧的DOM对象已经从DOM tree上移除,但并不会被GC回收,而是仍然被__resize__trigger__这个函数引用着,同时由于新生成的DOM不具有该属性,导致在L7引擎销毁的时候,由于L7找不到这个函数,resize事件解绑也会失败。在开发者触发多次切换引擎操作之后,有大量的未被实际引用的容器DOM无法被回收,而这些DOM中又都包含着webGL Canvas对象,导致浏览器的GLRendering资源不足的问题出现。

解决方法:我们无法修改L7的源码,因此也无法更改它注册、解绑事件的逻辑。但我们可以通过在每次Vue刷新之前,对即将被移除的canvas的width和height设置为0,以此来直接释放renderingContext资源,实测有效。

最佳解决方案:目前我们已经有自行实现的3D图形类,且也扩展了对Loca等其他可视化库的支持,可以摆脱对单一库的依赖,实现相同的能力。

四、多维数据比对

经过高德智慧交通大量的项目实践和数据比对,充分证明了地图空间可视化SDK开发的必要性,业务价值和技术价值都经历了项目的考验。以高德交通大脑和全境智能大屏的数据比对可以得到使用SDK之前和之后的数据比较:

项目落地效果:

使用SDK后的项目开发代码:

<template><!-- 地图底座 --><CommonMap:center="center":zoom="zoom":city="fitViewCity"view-mode="3D":map-style="mapStyle"><!-- 场景1 --><TrafficScene><!-- 地图元素层 --><TrafficPointLayer :list="trafficPointList" /><!-- 带有事件监听的地图元素层 --><TrafficRoadMarkerLayer:list="trafficRoadList"@click="handleTrafficRoadMarkerClick"/></TrafficScene><!-- 场景2 --><PublishScene><PublishMarkerLayer:list="publishList"@mouseover="handlePublishMouseOver"@mouseout="handlePublishMouseOut"@click="handlePublishClick"/></PublishScene><!-- 可视化场景 --><VisualizationScene use="amap-loca"><!-- 可视化数据层 --><TrafficRoadLineLayer :list="trafficRoadList" /></VisualizationScene></CommonMap>
</template>

五、展望

经过高德智慧交通大量项目的实践,SDK的建设已经趋于成熟,其开发简单、稳定性高、性能好的特点可以很好地降低开发者使用高德开放平台JSAPI来开发地图空间可视化项目的成本。我们未来会以开发者官网的形式对外输出,更好地服务于开发者。

招聘

阿里巴巴高德地图技术中心长期招聘Java、Golang、Python、Android、iOS 前端资深工程师和技术专家,职位地点:北京。欢迎投递简历到gdtech@alibaba-inc.com,邮件主题为:姓名-应聘团队-应聘方向。

高德智慧交通地图空间可视化SDK设计与实现相关推荐

  1. 智慧交通大数据可视化,让城市运营车辆可视、可监、可控

    交通是城市经济发展的动脉,与人们的日常生活息息相关,对城市经济.社会等方面的发展起着至关重要的作用.城市道路.公交.轨道交通等设施成为了城市交通的主要方式,但随着经济社会的高速发展和城市化进程的加快, ...

  2. 智慧交通大数据可视化

    交通是城市经济发展的动脉,与人们的日常生活息息相关,对城市经济.社会等方面的发展起着至关重要的作用.城市道路.公交.轨道交通等设施成为了城市交通的主要方式,但随着经济社会的高速发展和城市化进程的加快, ...

  3. 智慧交通大数据可视化管理平台建设方案

    "智慧交通大数据可视化与虚拟仿真突发事件应对管理决策服务平台"是一个针对交通管控单位的综合型辅助管理决策服务平台和三维仿真突发事件应对系统,系统以实际的交通设备和运输能力分布为基础 ...

  4. 智慧交通:监管可视化系统的解决方案

    前言 随着信息时代的发展变迁,荧幕里呈现的智慧城市慢慢出现了在现实生活中,很大程度上便利了日常的管理和维护.在智慧城市的大背景,智慧交通监管可视化系统是其重要的组成部分,通过一条条道路监控的串联,引申 ...

  5. 智慧交通 | 数字孪生可视化平台

    随着社会经济水平的提高,城市规模不断扩大,基建设施愈发完备.在向小康社会前进的路上,人们对幸福有更高的向往和需求. 近年来,机动车拥有量迅速增加,旅游人次频创新高,原来的交通供需平衡被打破,节假日.高 ...

  6. 高德智慧景区随身听播放器框架设计与实现

    简介:我们开发选型并没有采用传统的TTS技术(由文本内容生成机器语音),而是采用了更加通用音频格式(比如mp3),作为讲解的音频输入源,方便讲解者进行二次创作.本文将简单回顾高德智慧景区随身听播放器的 ...

  7. 智慧交通大数据分析工具是如何运作的

    随着我国城市化发展进程的加速,城市交通拥堵.交通污染日益严重,交通事故频繁发生.这些都是各大城市亟待解决的问题.众所周知,智能交通成为改善城市交通的关键策略.为了缓解城市交通中的各种问题,很多城市都采 ...

  8. 智慧交通指挥决策平台,让“城市大脑”尽收眼底

    近年来,经济社会的高速发展和城市化进程的加快,导致城市交通拥堵问题日趋严重,交通拥堵成为影响大城市居民出行的首要问题,交通事故数量呈上升趋势,机动车尾气污染成为城市大气污染的主要来源.目前很多城市都在 ...

  9. 未来交通已经“上线” 高德地图智慧交通合作已攻150城,半年大增约五成

    近日,高德地图宣布未来交通再下一城,联姻广东省公安厅推出网上车管所等6类24项功能的未来交通项目. 据记者了解,该项目至2017年12月31日已经完成试运行一月,效果明显,为广东省用户提供信息服务超过 ...

最新文章

  1. 低差异序列:范德科皮特序列(Van der Corput sequence)
  2. CG CTF WEB COOKIE
  3. 在MFC里面实现线程的实例
  4. strchr与strstr函数
  5. @requestbody和@requestparam作用
  6. 漫步凸分析六——凸集的相对内点
  7. python中sorted函数的作用_Python中排序方法sort、函数sorted的key参数的作用分析
  8. Kafka内核理解:消息的收集/消费机制
  9. 计算机组成原理(唐朔飞)--第一篇 概论
  10. STM32CubeMX | 37 - 使用RS485总线进行双板通信(SP3485)
  11. V-REP 添加Vision Sensor与图像获取 | V-REP Adding Vision Sensor and Image Acquisition
  12. 算法分析与设计——背包问题
  13. U盘格式化后容量变小恢复方法
  14. VB6-COM接口操作开源OpenOffice编辑Excel,Word文件(中国首创)
  15. 面试题:十瓶牛奶每天至少喝一瓶,直到喝完到底有多少种喝法
  16. 渐变多彩创意双十二活动方案主题PPT
  17. 解决QQ识图后复制文字然后将其粘贴到文件重命名中,总是显示重命名不成功
  18. android 恢复出厂,安卓(Android)手机恢复出厂设置的方法
  19. FPGA基础知识极简教程(10)二进制到BCD转换算法
  20. 继俄罗斯之后,中国也要拥有自己的 Sailfish 移动系统了

热门文章

  1. 腾讯面试居然跟我扯了半小时的CountDownLatch
  2. 云计算需要学习哪些内容 为什么要学容器技术
  3. FFmpeg提取视频中的音频
  4. 佛山市住房公积金数据异地容灾备份系统
  5. 实现文字色彩渐变(Mask)
  6. matlab声音信号调制-3种方法(附matlab代码)
  7. 产业互联网周报 | 上海数交所:数据产品交易额预计全年超过1亿元;Zoom单季度净利润同比下降86%;...
  8. Python学习周报
  9. 鸿蒙系统分享,鸿蒙系统的启动流程学习分享
  10. 自制拖把机器人_懒出新境界:可以自己洗拖布的机器人