Status: 收集信息中
Last Updated: 2022-01-20
背景
harp.gl 是 HERE 公司开源的一款基于 Three.js 搭建的地图渲染引擎。
因为最近在从事地图可视化方向,并且团队在使用和建设基于 Three.js 的技术栈,因此便有了一定的想法阅读它的源码,看能否得到一些思路。
初印象
harp.gl 是在 2018 年底,开始在 Github 上进行开发活动的。到目前也有差不多三年了。参见其 Github Contributors.
通过浏览它的基本示例的使用代码,不难发现它具有明显的 Three.js、Mapbox GL JS 的影子,甚至,在一些比较小的接口上,还有一些 Deck.gl 的影子。看来它在设计的时候,有较多地参考到这几个先于它出现的渲染引擎,前者是它基于的 3D 渲染引擎,后面二者是两个在地图领域被广泛使用的渲染引擎。
const canvas = document.getElementById('map');const mapView = new harp.MapView({canvas,theme: "https://unpkg.com/@here/harp-map-theme@latest/resources/berlin_tilezen_night_reduced.json"});// center the camera to New YorkmapView.lookAt({target: new harp.GeoCoordinates(40.70398928, -74.01319808),zoomLevel: 17,tilt: 40,});const mapControls = new harp.MapControls(mapView);const ui = new harp.MapControlsUI(mapControls);canvas.parentElement.appendChild(ui.domElement);mapView.resize(window.innerWidth, window.innerHeight);window.onresize = () => mapView.resize(window.innerWidth, window.innerHeight);const vectorTileDataSource = new harp.VectorTileDataSource({authenticationCode: 'YOUR-TOKEN HERE',});mapView.addDataSource(vectorTileDataSource);
其尝试走的商业模式,很 Mapbox GL JS 非常相似,目前处于 软件完全开源(Apache License 2.0),但是对于使用 HERE 的数据源,需要收费。不知道它未来会不会走类似 Mapbox GL JS 的路线,一旦软件使用的人多了,后期转变为了收费软件。但从现阶段看,其应用的广泛程度以及项目质量(不管是代码质量、文档、生态),还远不足以支撑它进入收费软件行列。
核心模块
MapView
整个引擎的入口模块为 MapView,其上挂载着 Three.js 的 Renderer、Scene、Camera,以及 harp.gl 自身定义的一众模块的 References,是一个最高层的集成模块。
其主要概念、行为的集成情况,如图所示:
MapView 集成图
关于其设计/代码风格的一些看法:
- 对于 THREE.Renderer 的构造参数,不是完整透传,而是选择性地传递了部分属性。
- Sub Renderers 其实只是一些 THREE.Object3D 构造器,不适合被称为 Renderer。实质上整个 MapView 只存在一个 THREE.Renderer 实例负责对所有 Objects 进行渲染。
- 大部分变量的命名做的比较好,能够让人很容易理解意图。
- 代码中存在大量的双向 References,模块间的依赖关系错综复杂,健康性和可维护性堪忧。
Tile 系列模块
Three.js 扩展模块
基于 Three.js 的模块进行扩展的模块
MapAnchor
- 对 THREE.Object3D 进行继承式扩展。而其实际扩展的功能,是一些地图领域的概念、拾取功能,以及样式。个人认为这是一种有损可组合性的扩展方式。
- 另外,因为扩展的字段都是optional的,所以从接口上说,它能够兼容 THREE.Object3D。
接口:
type MapAnchor<T extends THREE.Object3D = THREE.Object3D> = T & {
/**
* The position of this [[MapAnchor]] in {@link @here/harp-geoutils#GeoCoordinates}.
* @deprecated Use [[anchor]] instead.
*/
geoPosition?: GeoCoordinates;
/**
* The anchor of this Object3D in {@link @here/harp-geoutils#GeoCoordinates}
* or world coordinates.
*/
anchor?: GeoCoordLike | Vector3Like;
/**
* Flag defining if the object may be picked.
*
* @note By default all objects are pickable even if this flag is undefined.
*/
pickable?: boolean;
/**
* The styleSet that owns this map object.
*
* @remarks
* This property is used together with [[Theme.priorities]] to compute the render
* order of this map object.
*/
styleSet?: string;
/**
* The category of this style.
*
* @remarks
* This property is used together with [[Theme.priorities]] to compute the render
* order of this map object.
*/
category?: string;
/**
* Whether to draw the anchor on top of labels.
* @defaultValue false
*/
overlay?: boolean;
};
兼容 Object3D 的用途:
window.addEventListener("click", (evt) => {
//Create the three.js cube
const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00fe });
const cube = new THREE.Mesh(geometry, material);
cube.renderOrder = 100000;
//Get the position of the click
const geoPosition = mapView.getGeoCoordinatesAt(evt.pageX, evt.pageY);
cube.anchor = geoPosition;
// 此处的接口定义是 MapAnchor。
// 但因为 MapAnchor 在接口上兼容 THREE.Object3D,所以可添加 Object3D 实例。
//Add object to the map
mapView.mapAnchors.add(cube);
mapView.update();
});
工作机制
坐标参考系
herp.js 接口中的 GeoJSON,其坐标参考系使用的是 经纬度,并且坐标值顺序上,与通常的[Longitude, Latitude] 不同,它用的是 [Latitude, Longitude]。
Note: GeoJSON uses the format [Longitude, Latitude] for the coordinate system, while harp.gl uses [Latitude, Longitude]. Be careful not to get these confused!
与 GeoJSON 相关的关键模块
- GeoJsonDataProvider
- m_tiler 在处理 input
- this.m_tiler.registerIndex(this.name, this.input)
- ITile
- GeoJsonTiler
- updateIndex
- Mapbox 开发的 geojson-vt lib
- 功能:将 「GeoJSON」 转换成「MVT 的 JSON形式」。
- 用途:
- 从前端提高渲染大数据量时的性能;
- 后端用于生成 MVT。
- 局限性:GeoJSON-VT only operates on zoom levels up to 24.
- Mapbox 开发的 geojson-vt lib
- updateIndex
- m_tiler 在处理 input
渲染流程
MapView.render
MapView.render 流程图
瓦片管理
基于 MapView.render的流程分析,可初步判断,对于瓦片管理的关键函数,应该在于 VisibleTileSet.updateRenderList 和 TileObjectRenderer.render。
VisibleTileSet.updateRenderList 流程图
任务调度
(Hold on. )
MapViewTaskScheduler 做了什么?
分包机制
- @here/harp-mapview :绝大多数核心模块。
- @here/harp-datasource-protocol:这是个什么模块?datasource 的公共处理机制?
- @here/harp-vectortile-datasource:vector tile data source 相关模块。其实,内部也包含了 geojson data source 相关模块。
- @here/harp-xxx-datasource:各种 data source 模块。
- @here/harp-material:基于 Three.js Materail 进行扩展的自定义 Materials.
- @here/harp-utils :工具模块, 比如:
UriResolver。
应用分析
harp.gl 是否适合作为我们团队的地图可视化的基础架构?
harp.gl 中是否有某些模块,可以直接被我们用于填补 Three.js 到地图应用场景间的缺口?
TODO
- 搞清楚其地图领域关键问题的工作机制,以及模块的通用性
- 坐标参考系
- 数据源
- GeoJSON
- MVT
- 瓦片化
- 可以通过 各个包的 README/官方文档 来了解其中模块的职责。
