在之前的实例中设置图层的 source
属性时,可以发现经常用ol.source.OSM()
或 ol.source.BingMaps()
来创建图层的源。之所以加载源这么简单,是因为 OpenLayers 库中对 OSM(OpenStreetMap 地图)和 BingMap(必应地图)这类的国际比较常用的地图加载做了封装。但是 OpenLayers 不可能对所有的地图厂商都进行封装,这时候就需要自己手动拼接字符串来加载瓦片地图。绝大多数的瓦片地图都是用的大致相同的规律来加载的,这就使手动拼接字符串简单了许多。
LOD
介绍瓦片地图之前,首先应该了解 LOD
(Levels of Detail
),意思是多细节层次
。这是一种常用的游戏优化技术,按照模型的位置和重要程度来决定渲染时的资源分配,把非重要物体的面数和细节度降低,在渲染时就能更加高效。
在地图中,LOD
的概念也很重要,比如观察国家层次的地图时,街道信息就是我们不想要了解的信息。而且如果街道信息全部加载,地图一定非常的辣眼睛,设备也不一定能带的动。所以在不同的高度观察地图时,所需要展示信息的细节类型和程度都应该是不同的。所以对于同一个地点来说,在鼠标滚轮放大缩小处在不同层级时,其实是在眼皮底下完成了图片替换。
为了便于理解 GIS
系统中“层级的不同就是图片不同”这个概念,使用 google 的在线瓦片地图来说明。在为最小层级 0
的情况下,只使用了 1
张 256×256
像素的图片来表示整个地球平面:
放大一个层级,为 1
的时候,用了 4 张 256×256
像素的图片来表示整个地球:
瓦片计算
原理
虽然瓦片地图其实是利用了 LOD
技术实现图片替换,但是由 google 地图的例子可以发现,示例的层级是 0
级和 1
级,所需要的图片数目是 1
和 4
,也就是 2^0
和 2^2
。如果层级越大,显示整个地球时所需要的256×256
像素的图片就越多,n
层的图片数为 2^2n
,如下所示:
按照这个规律,如果层级为 10
,那么显示全球时所需要 256×256
像素的图片数是 1048576,这个数目是非常大的,全部展示在浏览器是不现实的。但是不管你的屏幕分辨率是 1920×1080
还是 1024×768
,一个浏览器的能同时显示的内容最大范围是固定的,所以只显示我们正在观察的这一张地图图片的附近若干张地图,就可以把屏幕全撑满,当移动或者放大缩小时,再加载当前中心位置附近的若干张,就实现了一个 WEB
地图。
日常中我们使用的网页地图,在大幅度移动或者缩放地图时,经常能够发现地图其实就是一张张加载出来,这种情况在网速不好的时候尤其明显,本质上就是新的地图图片正在加载。
瓦片坐标
当瓦片处于 n
层的时候,瓦片数目是 2^2n
,x
和 y
方向上都被切割成了 2^n
个。所以对于任何一个层级,都可以对它的瓦片像一个矩阵一样进行编号。
在 OpenLayers 的 source
中,有一个可以用于调试瓦片的源 ol.source.TileDebug,结合 OSM
的地图源可以清晰的观察到瓦片的坐标,在地图网格中,z
是层级,x
是经度方向的瓦片位置,y
是纬度的方向的瓦片位置。
代码如下:
1 | <div id="map" style="width: 100%;height: 100%;"></div> |
瓦片地图标准
在地图服务中,比较常用的两个标准如下:
WMTS
WMTS(Web Map Tile Service),是由OGC(Open Geospatial Consortium)制定的网页地图瓦片服务标准,标准中,原点在左上角(西北角),x
向右(东)为正方向,y
向下(南)为正方向。这也是 OpenLayers 中 ol.source.XYZ 使用的标准。
TMS
TMS(Tile Map Service),是由OSGeo(Open Source Geospatial Foundation)制定的地图瓦片服务标准,标准中,原点在左下角(西南角),x
向右(东)为正方向,y
向上(北)为正方向。
自定义瓦片地图加载
ol.source.XYZ
国内常用的一些 WEB 地图并没有被 OpenLayer 官方封装,但是 OpenLayers 中有一个ol.source.XYZ对象,ol.source.XYZ
对象的 url
属性 API 如下
URL template. Must include
{x}
,{y}
or{-y}
, and{z}
placeholders. A{?-?}
template pattern, for examplesubdomain{a-f}.domain.com
, may be used instead of defining each one separately in theurls
option.
翻译一下:
URL 模板。必须包含
{x}
,{y}
或{-y}
, 和{z}
占位符。一个{?-?}
模板模式,比如{a-f}.domain.com
。可以直接返回url
代替定义urls
属性。
有了这个 url
模板字符串,就可以很轻松的自定义一个 url
模板来读取各厂家地图。
高德地图示例
比如常用的高德地图,关于高德地图的服务地址规则可以参考下面这篇简书博客《高德 WMTS 瓦片地图服务地图图源规律》,分析一个典型的高德地图瓦片请求:
分析这个请求,protocol
是 https
,hostname
是 wprd02.is.autonavi.com
,path
中的/appmaptile
代表瓦片,query
中参数效果如下:
变量 | 说明 |
---|---|
hostname | 目前还没有找出规律(webst、webrd、wpst、wprd) |
lang | 可以通过 zh_cn 设置中文,en 设置英文 |
style | 地图类型控制,6(卫星图),7(简图),8(透明带道路详图) |
scl | 尺寸控制,1=256,2=512 |
ltype | 线性控制,增加后,只对地图要素进行控制,没有文字注记,要素多少,是否透明 |
知道了上述参数后,就可以很方便的来构造高德地图的 URL
:
1 | <div id="map" style="width: 100%;height: 100%;"></div> |
形成的地图如下所示:
ol.source.TileImage
并不是所有的厂家的地图像 OpenLayers 或者高德地图那样按照 WMTS
标准,比如百度地图的坐标系,原点在地图中心位置,向右为 x
正方向,向上为 y
正方向,而且分辨率也和 OpenLayers 的分辨率不同,此时就需要更基础的ol.source.TileImage来加载地图。主要需要的两个属性是tileGrid(瓦片网格)和tileUrlFunction(瓦片 URL 函数)。
百度地图示例
百度地图坐标系和 Openlayers 默认坐标系的主要区别是:
- 原点:百度坐标系原点在地图中心,OpenLayers 默认坐标系在地图左上角(西北角);
- y 方向:百度地图向上(北)为正方向,OpenLayers 默认坐标系向(下)南为正方向;
- 网格分辨率:百度地图每一层级网格的分辨率与 OpenLayers 默认不同;
- 投影格式:百度地图投影格式为 BD-MC,OpenLayers 默认为
EPSG:3857
;
首先看百度地图瓦片链接,这个链接是百度地图开发者平台的Javascript API
,里面有一个地图展示的 demo。如下图所示,打开控制台Network
,筛选请求为img
,然后随便在地图上拖动或者放大缩小,就会发现左下方列表里请求了新的瓦片图片。
http://maponline1.bdimg.com/tile/?qt=vtile&x=787&y=294&z=12&styles=pl&scaler=1&udt=20200211
分析这个请求,protocol
是 http
,hostname
是 maponline1.bdimg.com
,path
中的/tile
代表瓦片,query
是最关键的,x=787&y=294&z=12
说明 x>y>z
就是百度瓦片地图的模板字符串顺序。
加载百度地图代码如下:
- 原点:在 ol.tilegrid.TileGrid 设置
origin
坐标为[0, 0]
,也就是地图中央。 - 分辨率:根据百度地图网格分辨率规律递归计算出了一个分辨率数组,赋给了
ol.tilegrid.TileGrid
的resolutions
。 - 投影方式:百度地图使用的投影格式为BD-MC,使用
proj4
定义了BD-MC
的投影,并 ol.proj.proj4.register(proj4)注册给了 OpenLayers。 - xyz:在瓦片 URL 返回函数 tileUrlFunction 中,把y 坐标+1并加一个负号;再把所有为负数的
x
和y
的负号
换为M
。
1 | <div id="map" style="height: 100%;width: 100%;"></div> |
需要解密的瓦片地图
谷歌地图示例
首先说明因为地图服务商也是要恰饭的,所以建议大家最遵守版权和数据申明,通过申请开发者 API 合理合法的方式使用。
下面分析 google 加密后的地图 url
链接,加载 google 瓦片地图,通过调试工具拿到 url
如下:
很明显,两个瓦片 url
区别是 !2i
后的 211
和 212
以及 !3i
后的 105
和 106
,这就是 x
和 y
的位置。再缩放一下地图,就能发现 !1i
后的 8
就是层级 z
,所以就可以构造 url
,代码如下:
1 | <div id="map" style="height: 100%;width: 100%;"></div> |