1.综述
WebGIS中渲染地图时,要经常使用瓦片地图(tiled web map)。绝大多数瓦片地图使用的是EPSG:3857墨卡托投影,也有个别瓦片地图使用EPSG:4326投影。瓦片地图按比例尺从小到大划分,可分为22个级别(z)。每个级别中的所有瓦片都会拼接成一幅世界地图。当级别数为0的时候,一个瓦片囊括了整幅世界地图。级别数越大,能够显示的地理内容会更丰富。瓦片的横向索引(x)和纵向索引(y)的起始位置分别在最左边和最上边,数值均为0,并分别向右和向下+1递增。假设某一级别的级别数为z,则该级别的横向索引总数和纵向索引总数为。
以下表格展示了瓦片地图中的x、y、z参数。
参数 | 描述 |
z | 瓦片地图的级别,最小值为0。级别数越大,比例尺就越大,瓦片数就越多,显示的各个地理要素越丰富 |
x | 瓦片的横向索引,最小值为0。起始位置在最左边,向右+1递增。 |
y | 瓦片的纵向索引,最小值为0。起始位置在最上边,向下+1递增。 |
瓦片地图按数据显示结构,可以划分为两种形式:一个是矢量瓦片(Vector Tiles),另一个是栅格瓦片(Raster Tiles)。矢量瓦片主要为pbf格式,虽然能精确描述地理要素,但如果在大比例尺展示的情况下,会引发性能上的问题。而栅格瓦片是以图片的形式描述地理数据,尽管会有一些缺点(某些情况下地图要素和地图注记无法分离;一定时间范围内数据无法实时更新),但在显示的时候,第一次加载多个瓦片,第二次直接引用缓存。因此,一些主流的地理信息页面经常使用栅格瓦片作为地理底图。
2.具体实践
下面以OpenLayers为例,来实现地理底图的切换。实现页面如下:
![]() | ![]() |
![]() | ![]() |
首先,引用Openlayer的第三方插件。
<link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" type="text/css">
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
其次,在body标签里添加地图显示容器map,以及地理底图的下拉菜单。
<body>
<div id="map" class="map"></div>
<ul class="buttons">
<li onclick="tileClick(this)">OpenLayers默认地图</li>
<li onclick="tileClick(this)">谷歌地图</li>
<li onclick="tileClick(this)">天地图</li>
<li onclick="tileClick(this)">ArcGIS影像地图</li>
</ul>
</body>
其次,为各个组件定义CSS样式。为了使菜单在地图上显示,position设为绝对值。
.map {
width: 100%;
height: 100%;
position: relative;
}
.buttons {
position: absolute;
left: 0px; top: 0px;
float: left;
margin: 0px;
padding: 0px;
}
.buttons > li {
background: #990033;
list-style-type: none;
height: 25px;
color: white;
padding: 5px;
}
body {
margin: 0px;
}
最后开始在script里面编写代码了。
首先,定义栅格瓦片名称及服务路径,和地图图层。并假定当显示谷歌地图的时候就显示,其他底图则隐藏。x、y、z参数的含义前面已经提到了,这里不再赘述。
var mapSources = {
OpenLayers默认地图: "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",
谷歌地图: "http://mt2.google.cn/vt/lyrs=m&scale=1&hl=zh-CN&gl=cn&x={x}&y={y}&z={z}",
天地图: "http://t4.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}",
ArcGIS影像地图: "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
};
var layers = [];
for (var l in mapSources) {
let visible = false;
if (l === "谷歌地图")
visible = true;
layers.push(new ol.layer.Tile({
visible: visible,
title: l,
source: new ol.source.XYZ({
url: mapSources[l]
})
}));
}
接下来,为下拉菜单添加点击事件,当用户点击某个下拉菜单时,能够控制图层的显示。
function tileClick(e) {
for (let l = 0; l < layers.length; l++) {
if (layers[l].values_.title === e.innerText)
layers[l].setVisible(true);
else
layers[l].setVisible(false);
}
}
最后,为id为map的div控件设置地图展示的容器、图层、缩放的级别等。
var map = new ol.Map({
layers: layers,
target: "map",
view: new ol.View({
center: [0, 0],
zoom: 1
})
});
完整的代码如下:
<!DOCTYPE html>
<html>
<head>
<title>栅格瓦片切换</title>
<meta charset="utf-8">
<meta name="keywords" content="keyword1,keyword2,keyword3">
<meta name="description" content="this is my page">
<meta name="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="OpenLayers/v5.2.0-dist/ol.css" type="text/css">
<style>
.map {
width: 100%;
height: 100%;
position: relative;
}
.buttons {
position: absolute;
left: 0px; top: 0px;
float: left;
margin: 0px;
padding: 0px;
}
.buttons > li {
background: #990033;
list-style-type: none;
height: 25px;
color: white;
padding: 5px;
}
body {
margin: 0px;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<ul class="buttons">
<li onclick="tileClick(this)">OpenLayers默认地图</li>
<li onclick="tileClick(this)">谷歌地图</li>
<li onclick="tileClick(this)">天地图</li>
<li onclick="tileClick(this)">ArcGIS影像地图</li>
</ul>
</body>
<script src="OpenLayers/v5.2.0-dist/ol.js"></script>
<script type="text/javascript">
var mapSources = {
OpenLayers默认地图: "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png",
谷歌地图: "http://mt2.google.cn/vt/lyrs=m&scale=1&hl=zh-CN&gl=cn&x={x}&y={y}&z={z}",
天地图: "http://t4.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}",
ArcGIS影像地图: "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
};
var layers = [];
for (var l in mapSources) {
let visible = false;
if (l === "谷歌地图")
visible = true;
layers.push(new ol.layer.Tile({
visible: visible,
title: l,
source: new ol.source.XYZ({
url: mapSources[l]
})
}));
}
function tileClick(e) {
for (let l = 0; l < layers.length; l++) {
if (layers[l].values_.title === e.innerText)
layers[l].setVisible(true);
else
layers[l].setVisible(false);
}
}
var map = new ol.Map({
layers: layers,
target: "map",
view: new ol.View({
center: [0, 0],
zoom: 1
})
});
</script>
</html>
参考链接:
- 瓦片的定义(英文):https://wiki.openstreetmap.org/wiki/Tiles
- OpenLayers实例库(英文):http://openlayers.org/en/latest/examples/