我们在做可视化开发的时候常常需要下载某些地方的适量地图数据,利用echarts等去渲染相关数据,当然我们完全可以利用GIS技术去实现,但是GIS相对我们比较简单的需求成本有点过大,因为你需要制图、配准、发布,再到前段开发,这样有点得不偿失。

所以我们有时候仅仅需要一个县区的边界,比如我们需要展现“凤翔县”的行政边界我们现在可以这样做

Step 1

打开链接:百度地图API > 覆盖物示例 > 添加行政区划
http://lbsyun.baidu.com/jsdemo.htm#c1_10
看到如下界面:

注意上图的右边显示了一个北京市的边界。
再看左边的代码里有这么一段话:

1
2
3
4
5
6
7
8
9
10
function getBoundary(){       
var bdary = new BMap.Boundary();
bdary.get("北京市海淀区", function(rs){ //获取行政区域
map.clearOverlays(); //清除地图覆盖物
var count = rs.boundaries.length; //行政区域的点有多少个
if (count === 0) {
alert('未能获取当前输入行政区域');
return ;
}
……

通过一个GET请求去获取北京市海淀区的边界,我们打印出来看看,可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
function getBoundary(){       
var bdary = new BMap.Boundary();
bdary.get("北京市海淀区", function(rs){ //获取行政区域
//--------------------
console.log(rs)
//--------------------
map.clearOverlays(); //清除地图覆盖物
var count = rs.boundaries.length; //行政区域的点有多少个
if (count === 0) {
alert('未能获取当前输入行政区域');
return ;
}
……

在控制台看到的结果:

你会发现一个问题:控制台会输出一个数组,内部存储一个字符串,但是太长了没法复制,这里我们可以选择两个办法:

  1. 把这个结果存储到本地Local Storage里面,然后去那个里面复制。
  2. 请看Step 2

Step 2

改造上面的代码中getBoundary()函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function getBoundary(){       
var bdary = new BMap.Boundary();
bdary.get("凤翔县", function(rs){       //获取行政区域
map.clearOverlays();        //清除地图覆盖物       
var count = rs.boundaries.length; //行政区域的点有多少个
console.log(rs.boundaries);
var content = rs.boundaries
var fileName = '凤翔县.txt'
var aLink = document.createElement('a');
var blob = new Blob([content]);
var evt = document.createEvent("HTMLEvents");
evt.initEvent("click", false, false);
aLink.download = fileName;
aLink.href = URL.createObjectURL(blob);
aLink.dispatchEvent(evt);
aLink.click();
if (count === 0) {
alert('未能获取当前输入行政区域');
return ;
}
    var pointArray = [];
        for (var i = 0; i < count; i++) {
var ply = new BMap.Polygon(rs.boundaries[i], {strokeWeight: 2, strokeColor: "#ff0000"}); //建立多边形覆盖物
map.addOverlay(ply);  //添加覆盖物
pointArray = pointArray.concat(ply.getPath());
       }    
    map.setViewport(pointArray);    //调整视野  
    addlabel();               
});   
}

点击运行按钮等待2s会自动下载一个凤翔县.txt文件,打开后里面的内容如下

1
107.653733, 34.657435;107.648513, 34.661217;107.625176, 34.705117;107.609942, 34.707262;107.594553, 34.736579;107.584968, 34.730437;107.579062, 34.745847;107.573426, 34.745894;……

我们可以使用js的字符串切割方法对其进行以';'切割,之后再转换成二维数组,然后利用convas、svg技术描点绘制出来,其中类似107.653733就是维度利用js的经纬度转墨卡托算法(当然你也可以直接渲染,但保证地图上的点也是经纬度)在将其转换成墨卡托平面直角坐标系坐标,就可以了,当然会得出一个很大的值,我们可以除与共同系数来改变整体渲染结果的大小,我么还可以利用此去绑定鼠标事件。

经纬度转墨卡托算法

没接触过GIS的同学可能不知道这是什么意思,大致是这样的:

大家都知道,地球是个球体,那么经纬度明显表达的是一个度数,直接用其绘制出来的图形不是个平面的会有一定的变形,所以科学家墨卡托假想一个与地轴方向一致的圆柱切或割于地球,按等角条件,将经纬网投影到圆柱面上,将圆柱面展为平面后,即得投影。墨卡托投影在切圆柱投影与割圆柱投影中,最早也是最常用的是切圆柱投影。
算法公式在下面:

1
2
3
4
5
6
7
8
function _getMercator(poi) {//[114.32894, 30.585748]
var mercator = {};
var earthRad = 6378137.0;//赤道周长
mercator.x = poi.lng * Math.PI / 180 * earthRad;
var a = poi.lat * Math.PI / 180;
mercator.y = earthRad / 2 * Math.log((1.0 + Math.sin(a)) / (1.0 - Math.sin(a)));
return mercator; //[12727039.383734727, 3579066.6894065146]
}