1. 背景

1.1 地图瓦片之前

在地图瓦片技术使用之前,用户使用在线地图,一般都是客户端把将要显示的地理范围传送到服务端,服务器端将地理范围内的地理数据都查询出来,然后在服务端按照预先定义的专题地图的配置样式渲染成地图图片,再返回给客户端浏览,这也就是 OGC(地理信息联盟)标准的 WMS(Web Map Service)服务。

在 Web 时代 WMS 服务有几个大的缺点,由于用户浏览的地理范围是不确定性质的,获取范围内的数据是不确定,有时候数据会很大,数据从服务端的数据库到客户端的过程中,服务器 IO 操作和网络传输就很耗时;服务端获取数据后会进行数据渲染出图,占用大量 CPU 资源;用户频繁操作地图,服务端出图传输给客户端浏览过程中耗时非常长。

1.2 什么是地图瓦片

为了解决这些问题,谷歌地图率先提出了 TMS(Tile Map Service)服务,预先在服务端分层(金字塔模型)切片全量渲染,提出了 Web-Mercator 投影,按照地图不同的显示级别切成了瓦片坐标的图片。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hA7WJPQf-1658738538111)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*p1p8Sr8ZihIAAAAAAAAAAAAAARQnAQ “金字塔模型”)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u50A3QSo-1658738538112)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*brLxSa6NLBkAAAAAAAAAAAAAARQnAQ “瓦片坐标”)]

用户每次访问时候,根据当前的地理范围映射到瓦片坐标的图片索引(XYZ),然后从后端请求这些图片索引,客户端拿到图片后按照顺序依次渲染图片即可,整个过程中用户体验有明显的提升。很快 TMS 服务成为了 WebGIS 工业标准,在该技术的推动下,OGC(地理信息联盟)也发布了基于 TMS 的 WMTS 服务规范,国内外很多地图厂商也基于该技术生产了自己的切片地图服务。

1.3 栅格瓦片与矢量瓦片

栅格瓦片就是上述所说的地图瓦片,只是瓦片的数据形式是图片,以矢量数据组织的瓦片是矢量瓦片。栅格瓦片技术使用很广泛,也存在一些问。瓦片预切图时间长,数据更新不方便,按金字塔模型进行切图很耗时,每进入下一级别图片成 2^zoom 指数级别增加;服务端资源要求高,切片过程需要极大服务端渲染和切图的计算资源,存储数据冗余,在存储了原始数据外,还存储了冗余的切片数据。栅格瓦片地图样式单一,因为最终渲染是图片数据,不能满足自定义地图风格。

因上面这些问题 Mapbox 提出了 MVT 矢量瓦片切片技术,地图定制化极为方便,该技术目前最受欢迎,除二维地理数据切片外三维数据也深受此技术的影响诞生了 3DTiles 。当然说到这里这里瓦片预切图时间长与数据存储冗余并没有完全得倒解决,这里不再延续展开目前有的方案(数据动态切片与不切片)。

2. 瓦片地图生产

为了更好的使用瓦片服务,我们需要了解瓦片地图的生产过程,这里我们以栅格瓦片为例。下面分别以重要节点来简述这几个步骤。

2.1 数据投影及配图

2.1.1 数据投影

数据库中地理数据存储的形式一般都是大地坐标系(以纬度、经度和高度表示坐标元素),一般使用经纬度描述地球上点的位置,但是对于地图地理数据在二维平面内展示的场景,需要通过投影的方式将三维空间中的点映射到二维空间中,地图投影需要建立地球表面点与投影平面点的一一映射关系,映射方法有很多种,各有各的优缺点,互连网地图中常用 Web 墨卡托投影 ,它是墨卡托投影的变体。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iec9jfaL-1658738538113)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*JFCQQ7aeXKsAAAAAAAAAAAAAARQnAQ “墨卡托投影过程示意图”)]
Web 墨卡托投影是一个球面墨卡托的投影,它具有与球面墨卡托相同的属性,子午线都是等距的垂直线,角度在局部是正确的,而面积会随着离赤道越来越远而膨胀,以至于极地区域被严重夸大。由于 Web 墨卡托投影指定了在 WGS 84 椭球面模型上给出的测量坐标,但在投影的时候定义在球面上的,因此会有偏离,但在小比例尺民用场景精度可忽略不计,它主要的优点还是计算简单,数学运算复杂度低。

在缩放层级为 0 时,投影的过程就是将球面投影到一个正方形的平面上,这个平面就是世界坐标调整为左上角为(0, 0),右下角为 (256, 256)的正方形,假设单位为像数,那地图投影在一个 256px * 256px 的图片上。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qi7sPx0M-1658738538113)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*PyLRSrLi270AAAAAAAAAAAAAARQnAQ “Web 墨卡托投影”)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fYWxIg75-1658738538114)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*ShY0QJb3qq0AAAAAAAAAAAAAARQnAQ “球面墨卡托投影”)]

因为将极点投影在无穷远处,所以使用 Web墨卡托投影的地图无法显示极点,数据覆盖范围在经度 (-180°~180°),纬度 (-85.051129°~85.051129°)之间,投影的地图也是正方形。

2.1.2 地图配图

对数据进行投影之后,就需要根据不同的比例尺级别对数据进行配置,不同比例尺级别,显示的内容详略是不一样的,对数据进行分层级进行配图,比如层级大于 18 的时候,才显示一些街道级别的道路和 POI 等。再根据数据的不同类型进行样式配置,比如海洋填充显示为为淡蓝色,铁路显示为黑白相间的线段等。

数据的分层配图比较简单,但要配出一张好看的图需要处理好很多细节,专业的桌面软件有 ArcGIS、QGIS 等,感兴趣可体验简化在线版 Maputnik 配置矢量瓦片样式,这里不再展开。

2.2 地图切片

这里以谷歌地图规范的 TMS 为例,各地图厂商切片方式略有不一样,但都是基于 TMS 大同小异。

2.2.1 瓦片坐标系统

与谷歌地图相同的有高德地图、OpenStreetMap,一般都采用这种瓦片坐标系,将 Web 墨卡托投影地图的左上角作为瓦片坐标系起点,往左方向为 X 轴,X 轴与北纬 85.05º 重合且方向向左;往下方向为 Y 轴,Y 轴与东经 180º 重合且方向向下。瓦片坐标最小等级为 0 级,此时平面地图是一个像素为 256*256 的瓦片。在某一瓦片层级下,瓦片坐标的 X 轴和 Y 轴各有 2^zoom 个瓦片。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pX1KvYZ7-1658738538114)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*kBOUTJJgdjUAAAAAAAAAAAAAARQnAQ “TMS 瓦片坐标系统”)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bcGPRn1R-1658738538114)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*0DBFQIJM674AAAAAAAAAAAAAARQnAQ “WMTS 瓦片坐标系统”)]

WMTS 规范中,Web 墨卡托投影地图的左下角作为瓦片坐标系起点,也就是坐标系原点为东经 180°,南纬 85.05°,X 轴向右,Y 轴向上,瓦片坐标最小等级也从 0 级开始,常见使用该规范的地图服务有天地图、腾讯地图、ArcGIS、超图等。
需要注意的是,微软的必应地图使用的编码规范,Z 的定义与谷歌相同,同一层级的瓦片不用 XY 两个维度表示,而只用一个整数表示,该整数服从四叉树编码规则;百度地图的,Z从 1 开始,在最高级就把地图分为四块瓦片,XY 的原点在经纬度为 0 的位置,X从左向右,Y从下向上。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1BaHGkBF-1658738538115)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*cKJARZPC0uYAAAAAAAAAAAAAARQnAQ “必应地图”)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HMyB1YEJ-1658738538115)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*LKSASaPKkm0AAAAAAAAAAAAAARQnAQ “百度地图”)]

2.2.2 坐标转换过程

在了解了数据投影后,我们需要将经纬度坐标转换为瓦片坐标,Web 墨卡托的投影计算公式如下图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bZs57Kem-1658738538116)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*X89SQ6TjXZUAAAAAAAAAAAAAARQnAQ “Web 墨卡托的投影计算公式”)]
公示中 λ 是用弧度表示的经度,而 φ 是用弧度表示的纬度。经纬度坐标转换为瓦片坐标其推导过程为 经纬度 => 米 => 像素坐标 => 瓦片坐标

经纬度 => 米
将经纬度转为球面 Web 墨卡托投影的米制单位,投影到正方形
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v0pjauvf-1658738538116)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*CJ8PQItUa8QAAAAAAAAAAAAAARQnAQ)]

const LatLongToMeterXY = (longitude: number, latitude: number) => {// 地球的周长的一半 20037508.342789244 单位米const circumferenceHalf = Math.PI * 2 * 6378137 / 2.0const mx = longitude * circumferenceHalf / 180const temp = Math.log(Math.tan((90 + latitude) * (Math.PI / 360.0))) / (Math.PI / 180.0)const my = circumferenceHalf * temp / 180return { mx, my }
}// [-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244]

米 => 像素坐标

const MetersToPixelXY = (mx: number, my: number, zoom: number, tileSize = 256) => {// 地球的周长的一半 20037508.342789244 单位米const circumferenceHalf = Math.PI * 2 * 6378137 / 2.0// 米/每像素const resolution = Math.PI * 2 * 6378137 / (tileSize * Math.pow(2, zoom))const px = (mx + circumferenceHalf) / resolutionconst py = (my + circumferenceHalf) / resolutionreturn { px, py }
}

像素坐标 => 瓦片坐标

const PixelXYToTileXY = (px: number, py: number, tileSize = 256) => {const tx = Math.floor(px / tileSize);const ty = Math.floor(py / tileSize);return { tx, ty }
}

2.2.3 经纬度与瓦片坐标互转

根据上面的推导过程,很容易算出经纬度坐标转瓦片坐标,瓦片坐标转经纬度坐标的公式,转换公式如下:
经纬度 => 瓦片坐标
![image.png](https://img-blog.csdnimg.cn/img_convert/e4c912a89a1d81f8753630b89f318844.png#clientId=u7bd649aa-7765-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=152&id=S6n9G&name=image.png&originHeight=600&originWidth=1500&originalType=binary&ratio=1&rotation=0&showTitle=true&size=60082&status=done&style=none&taskId=uf1915bb6-1bba-44d1-8d22-44f019abd8f&title=Slippy map tilenames - Latlon to tile&width=380 “Slippy map tilenames - Latlon to tile”)

const LatLongToTileXY = (longitude: number, latitude: number, zoom: number) => {// 地球的周长的一半 20037508.342789244 单位米const circumferenceHalf = Math.PI * 6378137;const mx = (longitude * circumferenceHalf) / 180;const temp = Math.log(Math.tan((90 + latitude) * (Math.PI / 360.0))) / (Math.PI / 180.0);const my = (circumferenceHalf * temp) / 180;// 米/每像素const resolution = (Math.PI * 2 * 6378137) / (tileSize * Math.pow(2, zoom));// px = (mx + circumferenceHalf) / resolutionconst px = ((longitude + 180) / 360) * Math.pow(2, zoom) * tileSize;// py = (my + circumferenceHalf) / resolutionconst py = ((circumferenceHalf * temp) / 180 + circumferenceHalf) / (Math.PI * 2 * 6378137) / (tileSize * Math.pow(2, zoom));// tx = Math.floor(px / tileSize)const tx = Math.floor(((longitude + 180) / 360) * Math.pow(2, zoom));// ty = (1 - arsinh(tan(latitude * π / 180)) / π) * Math.pow(2, zoom)// 双曲正弦函数 arsinh(x) => ln(x + (x^2 + 1)^0.5)const ty = Math.floor(((1 -Math.log(Math.tan((latitude * Math.PI) / 180) +1 / Math.cos((latitude * Math.PI) / 180)) /Math.PI) /2) *Math.pow(2, zoom));return { tx, ty };
};

瓦片坐标 => 经纬度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X8rBrI1D-1658738538117)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*CJ8PQItUa8QAAAAAAAAAAAAAARQnAQ “Slippy map tilenames - Tile to latlon”)]

const TileXYToLatLong = (tx: number, ty: number, zoom: number) => {const longitude = (x / Math.pow(2, zoom)) * 360 - 180;const tmp = Math.PI - (2 * Math.PI * y) / Math.pow(2, zoom);// latitude = (180 / Math.PI) * Math.atan(sinh(tmp))// 双曲函数sinh(x)=(e^x - e^(-x)) * 0.5const latitude =  (180 / Math.PI) * Math.atan(0.5 * (Math.exp(tmp) - Math.exp(-tmp)));return { longitude, latitude };
};

2.3 瓦片地图服务发布

切片完成后的瓦片文件在一个大目录下,一般每个缩放层级是一个目录,每列是一个子目录,并且该列中的每个瓦片是一个文件,每个瓦片文件的路径名一般为缩放层级 + 列号 + 行号,比如 /z/x/y.png 这种形式,客户端通过拼接服务的域名加文件名来请求对应的瓦片。一般服务器考虑到并发请求原因会对地图瓦片的服务器会提供几个子域名,比如 OSM 地图服务:

  • A 子域名:https://a.tile.openstreetmap.org/14/13659/6746.png
  • B 子域名:https://b.tile.openstreetmap.org/14/13659/6746.png

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ydsn33NI-1658738538118)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*ADeRRLx_0-YAAAAAAAAAAAAAARQnAQ)]

通过上面的网址都可以获得这个瓦片图片,从地址中可以看出,这个瓦片的列 X 是 6745,行 Y 是 3103,缩放曾经 Z 是 13。最终我们就可以把这些瓦片文件通过静态文件服务器或 CDN 发布出去给客户端使用。

3. 瓦片地图服务解析

在了解完瓦片生产过程后,我们能明白数据是怎么投影的及地图是怎么切片的,对客户端加载解析瓦片服务的理解更加容易了。客户端要想渲染出地图有这么几个简单流程:

  • 获取当前地图的地理范围及当前地图的缩放层级
  • 通过上面两个参数计算出所有瓦片坐标(瓦片索引)
  • 通过瓦片的服务地址和瓦片坐标,拿到所有瓦片的数据(栅格数据或矢量数据,如果是栅格瓦片服务也就是图片)
  • 按顺序拼接好瓦片渲染出来

3.1 瓦片服务解析流程设计

为了保证瓦片数据解析流程高可用我们需要满足以下几重要点:

  • 栅格瓦片和矢量瓦片服务都能使用
  • 优化性能,避免频繁请求瓦片数据,瓦片数据能够缓存
  • 体验优化,瓦片更新时支持渐近更新,避免白屏网格
  • 支持设置缓冲区,提前预先加载瓦片
  • 支持对称子午线地图重复,瓦片连贯显示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wn921q0g-1658738538118)(https://gw.alipayobjects.com/mdn/rms_5e897d/afts/img/A*v7FeT64Z30sAAAAAAAAAAAAAARQnAQ “瓦片服务的解析、加载、使用流程设计”)]
更多的设计实现详见

WebGIS 瓦片地图引擎实现之——地图瓦片计算相关推荐

  1. 开源地图引擎openlayers_开源地图简单对比

    开源地图引擎 开源协议 地图引擎对比数据获取时间 2020-8-10name-mapleafletopenlayermapbox-glcesium周下载量3740149950735228916903 ...

  2. WebGIS 瓦片地图引擎实现之——地图瓦片加载计算原理介绍

    1. 背景 1.1 地图瓦片之前 在地图瓦片技术使用之前,用户使用在线地图,一般都是客户端把将要显示的地理范围传送到服务端,服务器端将地理范围内的地理数据都查询出来,然后在服务端按照预先定义的专题地图 ...

  3. arcengine 加载地图不显示_地图建筑建模制作与输出

    导读 阅读完此文,你会了解: 1.地图建筑模型通常如何制作的 2.地图建筑模型替换策略 地图上往往会有一些定制建筑的需求,例如将下面的水立方做成气泡感的. 加入定制模型之前 加入定制模型之后 这种需求 ...

  4. 最简单的WebGIS地图引擎,最丰富的历史地理知识

    无意中发现了一个Web地图,可以称的上是一个简单的web地图引擎,先看看界面,下载链接在最后: 战国时期世界版图 西汉汉武帝时期世界版图 大唐帝国时期世界版图 清朝康熙年间世界版图 放大一级有国家领导 ...

  5. arcgis制作瓦片地图_一种GIS瓦片地图的存储方式的制作方法

    本发明涉及一项测绘与地理信息行业数据存储方式. 背景技术: 目前主要使用的GIS瓦片存储技术有两种一种是数据库文件存储,另一种是压缩包存储方式:数据库存储文件方式存在存储数据的冗余,数据格式体积较大的 ...

  6. python下载谷歌地图瓦片_使用 Python 合并地图瓦片

    前文提到了合并瓦片图,而瓦片图应用比较多的则是瓦片地图.对地图本就感兴趣的我,也想试试合并互联网地图的某个范围内的地图图层. 随着技术的发展,国内的地图服务商相继将地图瓦片更新为矢量瓦片[1],这下想 ...

  7. 服务器引擎制作,2.5D-GIS地图引擎设计

    1.    2.5D地图概述 1.1.    概述 2.5维地图就是根据dem.dom.dlg等数据,以及真三维模型在一定高度.视角和灯光效果,按照轴侧投影的方式生成的地图.本文以臻图信息ZTMapE ...

  8. 从零打造一个Web地图引擎

    说到地图,大家一定很熟悉,平时应该都使用过百度地图.高德地图.腾讯地图等,如果涉及到地图相关的开发需求,也有很多选择,比如前面的几个地图都会提供一套js API,此外也有一些开源地图框架可以使用,比如 ...

  9. LibGDX游戏引擎-10-游戏地图(TiledMap)

    转载自:http://www.qiushurong.cn/2014/03/23/tiledmap/ 要做游戏地图,在libgdx中我们使用到的工具是TiledMap Editor,官方网址是:链接 T ...

  10. Qt quick-QML地图引擎之v3版本(新增高德/谷歌在线/离线预览/多线程离线裁剪下载/区域查询/位置搜索/路径规划)

    在上个版本64.qt quick-qml使用高德地图插件实现V2版本(新增:位置搜索.路径规划.轨迹编辑等)_诺谦的博客-CSDN博客_qt高德地图插件基础下新增以下功能: 1.支持多线程请求.超时重 ...

最新文章

  1. Java随机字符串:随机数字字符串,工具类
  2. 基于Matlab的多层BP神经网络在非线性函数拟合中的应用
  3. 食出100分:‘粥’的做法4---鱼片瘦肉粥
  4. python入门教程非常详细-Python该怎么入门?Python入门教程(非常详细)
  5. linux 真实内存,Linux计算真实可用内存
  6. [Java2入门经典]第9章 访问文件和目录
  7. 微型计算机中常体积的,微型计算机原理及应用第2章.ppt
  8. 微信扫码支付、聚合支付
  9. vs2003无法打开sal.h
  10. 微软梁念坚谈新平台 企业跨界办公随需而变
  11. 将视频文件旋转90°的方法
  12. 温莎大学的计算机专业,温莎大学的计算机专业
  13. 区块链相关的关键概念
  14. 使用神经网络和遗传算法玩转 Flappy Bird
  15. DPC(Defect Pixel Correction)——坏点检测
  16. cad能整体比例缩小吗_如何用cad将图整体比例放大
  17. android 4.2目录结构,关于android的4.2的0文件夹的详解(目录结构挂载分析)
  18. python学习--DataFrame
  19. java-把最后一个two单词首字母大写
  20. 大学计算机类专业按成绩分,那个大学的计算机专业比较好

热门文章

  1. win10怎么做文件服务器,win10怎么做云服务器
  2. Oracle Coherence中文教程三:配置
  3. Android WebView 跳转第三方App
  4. BioGRID:蛋白质相互作用数据库
  5. uploadify onSelect
  6. python爬虫--看看虎牙女主播中谁最“顶”
  7. 爬虫实战1:爬取哔哩哔哩主播的头像以昵称命名保存到本地文件
  8. php带图片的每日单词,GRE背单词-每日十个单词(第一天) - 英语家园
  9. Shopee平台:跨境卖家要怎么运营才能做好基础销量?
  10. 北京邮电大学计算机学院考研夏令营,北京邮电大学计算机学院(专业学位)计算机技术保研夏令营...