关于ArcGIS调用天地图的方法,还有两篇博客可以参考,一个是esri针对SDK for Android,另一个是LZU-GIS扩展dojo。这里我在这两篇文章的基础上提供一种直接调用相关API的方法,基于ArcGIS API for JavaScript v3.18。
一、WMTS服务
WMTS服务即网络地图瓦片服务,不同于WMS、WFS、WCS可以简单一句url的GET请求调用一张地图,它本质上是很多张地图不断地被访问调用,这一点可以使用浏览器F12查看网络通讯证实。那么WMTS究竟是怎么样的服务呢?我们可以先使用GetCapabilities操作查看一下。
天地图的经纬度投影全球地图矢量服务请求url如下:http://t0.tianditu.com/vec_c/wmts?request=GetCapabilities&version=1.0.0&service=wmts。根据<ows:OperationMetada>可知其核心操作是GetTile。注意到和<ows:OperationMetada>同层子标签的还有一个<Contents>,其子标签又有<Layer>和<TileMatrix>,稍微看一下就能大概知道,前者是图层信息,后者是具体的瓦片划分信息。
部分标签具体的解释还可参见超图的文档http://support.supermap.com.cn/DataWarehouse/WebDocHelp/6.1.3/iserverOnlineHelp/API/WMTS/wmts_introduce.htm。
二、JavaScript调用WMTS
ArcGIS调用WMTS的代码可以查看官方示例代码WMTS-resource info这一节。乍一看会觉得这段代码怎么这么长,有这么多的东西。让我们具体了解一下这些代码,尤其是中间一长串的level、scale、resolution都是什么意义。根据API文档的WMTSLayer类部分,其构造函数需要传参WMTS服务器url,以及一个选项参数,后者包括服务模式(本例中是KVP)、resourceInfo和layerInfo。我们具体看看后面两项。
var tileInfo1 = new TileInfo({
"dpi": 90.71428571428571,
"format": "image/png",
"compressionQuality": 0,
"spatialReference": new SpatialReference({
"wkid": 3857
}),
"rows": 256,
"cols": 256,
"origin": {
"x": -20037508.34,
"y": 20037508.34
},
"lods": [{
"level": "EPSG:900913:8",
"scale": 2183915.0935581755,
"resolution": 611.4962261962892
}, {
"level": "EPSG:900913:9",
"scale": 1091957.5467790877,
"resolution": 305.7481130981446
}, {
"level": "EPSG:900913:10",
"scale": 545978.7733895439,
"resolution": 152.8740565490723
}, {
"level": "EPSG:900913:11",
"scale": 272989.38669477194,
"resolution": 76.43702827453615
}, {
"level": "EPSG:900913:12",
"scale": 136494.69334738597,
"resolution": 38.21851413726807
}, {
"level": "EPSG:900913:13",
"scale": 68247.34667369298,
"resolution": 19.109257068634037
}, {
"level": "EPSG:900913:14",
"scale": 34123.67333684649,
"resolution": 9.554628534317018
}, {
"level": "EPSG:900913:15",
"scale": 17061.836668423246,
"resolution": 4.777314267158509
}, {
"level": "EPSG:900913:16",
"scale": 8530.918334211623,
"resolution": 2.3886571335792546
}, {
"level": "EPSG:900913:17",
"scale": 4265.4591671058115,
"resolution": 1.1943285667896273
}, {
"level": "EPSG:900913:18",
"scale": 2132.7295835529058,
"resolution": 0.5971642833948136
}, {
"level": "EPSG:900913:19",
"scale": 1066.3647917764529,
"resolution": 0.2985821416974068
}]
});
首先是resourceInfo,文档对该参数的解释是,WMTSLayer对象缺省的行为是使用一个代理页面产生GetCapabilities请求(以此来获取资源信息),如果指定了resourceInfo参数,就不需要这一步骤了。也就是说,这一参数是在指定WMTS的资源信息。resourceInfo参数具体又包括了版本号、layerInfos等。后者是WMTSLayerInfo的对象数组,所以我们再来看看WMTSLayerInfo类。根据前面的实例代码,可以发现中间那一长串其实就是这里的构造参数tileInfo的一部分。还记得天地图WMTS的GetCapabilities操作返回的xml吗,眼前的这串字符是否眼熟?如果你对示例代码中的WMTS服务使用GetCapabilities服务,并且在返回xml中查找实例代码中的任意的"scale"如545978.7734655447,你会发现这个数字其实就是一个<TileMatrix>下的<ScaleDenominator>值。这些level和scale(resolution在下面介绍)其实就是返回xml中的部分层级瓦片比例尺。同理,WMTSLayerInfo的其他构造参数如identifier、tileMatrixSet也可以在返回xml中获取。这就是为什么使用resourceInfo参数就不用再发送GetCapabilities请求,因为这个参数本质上就是后者返回信息的子集。
再看layerInfo参数就很简单了。resourceInfo是所有加载的图层资源的数组,而layerInfo就是具体显示的图层。
那么上面没有说明的resolution又是什么呢?这个数字在GetCapabilities的返回值中并没有出现,其实这是一个计算的结果。公式如下,这里dpi是屏幕分辨率,0.0254是米与英寸的单位换算:
如果你带入上面代码的数值计算,公式是可以成立的。只是要注意三点:
1、代码中的scale在xml返回结果中的参数是<ScaleDenominator>,也就是说是比例尺分母。真正公式中的Scale应该是<ScaleDenominator>的倒数。
2、这里dpi带入90.71428571428571公式成立是因为该坐标系是EPSG:900913,若为EPSG:4326,则dpi应该是90.71428571428571*111194.65190665461376212444103004,后者是ArcGIS采用的经纬度单位1与米的转换系数。
3、这里dpi采用的是90.71428571428571是OGC WMTS规范中的数值
三、JavaScript调用天地图WMTS
接上部分,天地图采用的dpi则是国标规定的96,这一点通过开头所说两篇博客中的scale、resolution也可反算出来。
好了,所有需要了解的我们都知道了,我们来根据GetCapabilities返回xml改写示例代码。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
<title>test</title>
<style>
html, body { height: 100%; width: 100%; margin: 0; padding: 0; }
#map { padding:0; }
#citationInfo { position:absolute; color:black; font-weight:bold; font-size:12pt; left:10px; bottom:10px; z-Index:999; }
</style>
<link rel="stylesheet" href="https://js.arcgis.com/3.18/esri/css/esri.css">
<script type="text/javascript" src="https://js.arcgis.com/3.18compact/"></script>
<script type=