地图渲染

什么是地图?

通俗的说地图就是地球的画像。

什么是地图渲染?

地图渲染给地图的画像的过程,就是按照一定的比例关系、投影方法,使用一定的符号,把现地球上的山川、道路、建筑等信息在纸或屏幕上描绘出来。

互联网地图的表达方式

投影

球面到平面。

Web墨卡托投影

假设地图中心有一个灯,用一张纸沿着地球赤道包裹起地球,这样地球上每个点都会映射到纸上,这样就得到摩卡拖坐标系。

X轴:由于赤道半径为6378137米,则赤道周长为2*PI*r = 2*20037508.3427892,因此X轴的取值范围:[-20037508.3427892,20037508.3427892]

Y轴:由墨卡托投影的公式可知,同时上图也有示意,当纬度接近两极,即90°时,y值趋向于无穷。为了便于计算,“懒惰的工程师”就把Y轴的取值范围也限定在[-20037508.3427892,20037508.3427892]之间,搞个正方形。

正方形地图

切片与分层

为了避免一次加载全部地图数据,实现按需加载地图,这里需要对地图进行分层和切片。

分层切片

地图渲染的过程

地图渲染总体结构

  1. 获取数据
  2. 数据预处理
  3. 配置样式
  4. 渲染

获取数据

数据包括什么

地图使用点、线、面描绘现实世界,目前地图数据主要包括

如何获取数据

数据预处理

数据抽稀 道格拉斯-普克算法

Douglas-Peucker_animated

  1. 在曲线首尾两点间虚连一条直线,求出其余各点到该直线的距离。
  2. 选其最大者与阈值相比较,若大于阈值,则离该直线距离最大的点保留,否则将直线两端点间各点全部舍去。
  3. 依据所保留的点,将已知曲线分成两部分处理,重复第1、2步操作,迭代操作,即仍选距离最大者与阈值比较,依次取舍,直到无点可舍去,最后得到满足给定精度限差的曲线点坐标。

POI Rank

  1. 根据类型配置,通常是一些经验值,比如火车站就比餐馆更重要。
  2. 根据父子关系计算。
  3. 根据用户的搜索量。

几何对象相识度

面相似度 = A交B面积 / A并B面积

点和线也可以通过生成缓冲区转化成面来计算

配置样式

样式是来定义地图包含那些图层,这些图层使用哪些数据,在哪些层显示,怎么显示的。一个简单的样式示例如下

<Map background-color="blue" srs="+init=epsg:4326">
  <Style name="My Style">
    <Rule>
      <PolygonSymbolizer fill="#f2eff9" />
      <LineSymbolizer stroke="rgb(50%,50%,50%)" stroke-width="0.1" />
    </Rule>
  </Style>
  <Layer name="world" srs="+init=epsg:4326">
    <StyleName>My Style</StyleName>
    <Datasource>
      <Parameter name="file">path/to/shapefile.shp</Parameter>
      <Parameter name="type">shape</Parameter>
    </Datasource>
  </Layer>
</Map>

渲染

渲染栅格瓦片

渲染的过程

  1. 逐个层次,逐个瓦片进行渲染(1到5步)。
  2. 从样式中逐个获取图层。
  3. 读取图层在当前瓦片范围的数据,并进过平移缩放转化成瓦片上的坐标。
  4. 准备画布画笔画刷等,逐条绘制几何数据。
  5. 最终生成渲染好的栅格瓦片地图。
  6. 并发布成地图服务。

最终提供瓦片地图服务(Tile Map Service)。前端根据当前的位置,计算需要哪些瓦片的数据,下载这些瓦片进行拼接。

并行渲染

由于瓦片的数量非常的多,如果切18级一共包括14亿多个小瓦片。这是非常巨大的运算量。由于每个渲染瓦片都是相对独立的,因此可以并行计算。同时为了减少计算的次数,可以一次渲染多个(16*16)瓦片。

并行渲染

我们当时使用Hadoop来进行并行渲染,也可以使用RabbitMQ这样的消息队列实现。Map对原始数据进行切片,Reduce进行渲染栅格瓦片。使用2000个节点、全国切图用时在5个小时以内,生成的瓦片在180G样子。

遇到的问题

解决办法

演进到矢量瓦片

由于栅格瓦片使用的数据流量大,不支持地图的旋转、3维效果,难以支持多种地图样式,加上前端的渲染性能已逐步提高,目前绝大多数的App已经是前端渲染。

与后端渲染不同的是,前端渲染在切图的过程中并不会生成栅格瓦片,而是生成矢量瓦片。然后由前端根据样式文件通过OpenGL ES或WebGL完成渲染。

矢量瓦片格式

矢量瓦片文件采用Google Protocol Buffers进行编码。它包括若干图层,每个图层包含若干要素。要素是由几何图形和属性信息组成的。

几何图形编码

使用瓦片坐标系,把浮点型坐标转化成整形坐标。瓦片的左上角是坐标系的原点。X轴向右为正,Y轴向下为正。

一个简单的矢量瓦片示例

layers {
  version: 2
  name: "points"
  features: {
    id: 1
    tags: 0
    tags: 0
    type: Point
    geometry: 9
    geometry: 2410
    geometry: 3080
  }
  keys: "hello"
  values: {
    string_value: "world"
  }
  extent: 4096
}
OpenGL渲染

opengl_pipeline

难点

数据更新

由于一次渲染耗时耗时还是相对较长的。在对于新高速通车、新地铁通车等实时性更新要求比较高的情况下,可以增量更新部分区域的瓦片。如果数据有定期的增量更新包(Diff数据),也可以实现实时更新的地图。

增量更新

comments powered by Disqus