目录
前言
"地图是世界的索引,而地名则是索引中的索引。"当互联网地图进入 Web 2.0 时代,开发者不再满足于"看得见"的瓦片,更希望"问得准"------输入一个模糊的地名,就能在毫秒之间锁定它在全球、全国、省市、区县乃至乡镇的多级位置。天地图作为国家地理信息公共服务平台的官方出口,提供了完全符合国标的行政区划与兴趣点(POI)检索能力;而轻量级的 Leaflet 则在前端以极简的 API 哲学,让地图可视化回归"纯粹的快乐"。当两者相遇,我们便有了"用 200 行代码完成一个国家级别地名搜索引擎"的可能。本文所记录的,正是这样一次"小而美"的实战:如何以 Leaflet 为壳、天地图为芯,逐级剖开中国行政体系的"套娃式"结构,把"模糊输入--多级联想--精准定位"做成一套可复用的前端组件。在WebGIS的可视化效果中,在省级节点中展示统计数量,随着地图的深入逐级进行细化,将地名逐渐呈现在大家的面前。

在之前的博客中曾经对如何调用天地图的检索API进行了详细的介绍。但是如何进行详细层级展示没有进行详细的说明,因此有朋友留言表示希望可以对层级展示进行一个具体的说明。本文即在此背景下产生。
阅读全文并运行示例后,你将掌握:
如何正确调用天地图全部检索接口;
如何为 Leaflet 编写一个"可复用"的入口,支持调用天地图的地名检索接口;
如何在Javascript中处理层级展示问题;
如何实现下钻的行政区检索。
一、天地图在线检索
本节将详细的介绍天地图的在线检索功能,分别介绍天地图的搜索功能和后端接口。通过参考学习天地图的地图展现形式,为我们后面的技术和页面实现提供参考。
1、在线检索功能
为了展示天地图的多层级展示功能,首先我们来看一下其官方网站的实例。大家可以访问天地图后,点击"在线"地图的tab标签页,从而跳转到在线地图,如下图所示:

在其页面的左上角就有一个输入框,在这个输入框中输入相应的信息就可以完成信息的检索。比如在输入框中输入"自然",点击检索,在界面上就可以看到以下的展示:

可以看到包含了自然的地名最多的省级行政区划是浙江省,共有599616条记录。第二名是广东省,约有3307条记录,第三名是江苏省,大约有2842条记录。
2、再谈后后接口
为了照顾第一次看博文的朋友,这里将天地图的检索接口API给大家详细的介绍一下,让大家了解后台的数据接口和服务,在下一节的多层级调用展示过程实现提供技术基础。
输入参数说明
|-----------|-----------------------------------------|----------|----------|--------------------------------------------------------------------------------------------------------------|
| 参数值 | 参数说明 | 参数类型 | 是否必备 | 备注(值域) |
| keyWord | 搜索的关键字 | String | 必填 | 无 |
| specify | 指定行政区的国标码(行政区划编码表)严格按照行政区划编码表中的(名称,gb码) | String | 必填 | 下载行政区划编码表。9位国标码,如:北京:156110000或北京。 |
| queryType | 服务查询类型参数 | String | 必填 | 12:行政区划区域搜索服务。 |
| start | 返回结果起始位(用于分页和缓存)默认0 | String | 必填 | 0-300,表示返回结果的起始位置。 |
| count | 返回的结果数量(用于分页和缓存) | String | 必填 | 1-300,返回结果的条数。 |
| dataTypes | 数据分类(分类编码表) | String | 可选 | 下载分类编码表,参数可以分类名称或分类编码。多个分类用","隔开(英文逗号)。 |
| show | 返回poi结果信息类别 | String | 可选 | 取值为1,则返回基本poi信息; 取值为2,则返回详细poi信息 |
返回参数说明
|---|---|-------------|---------------|-------------|----------|---------------------------------------------------------|
| 参数值 ||| 参数说明 | 参数类型 | 返回条件 | 备注(值域) |
| resultType ||| 返回结果类型 | Int | 必返回 | 取值1-5,对应不同的响应类型: 1(普通POI),2(统计),3(行政区),4(建议词搜索),5(线路结果) |
| count ||| 返回总条数 | Int | 必返回 | |
| keyword ||| 搜索关键词 | String | 必返回 | 搜索的关键字。 |
| pois ||| 针对点(类型1)集合返回 | Pois Json数组 | | resultType=1 |
| | name || Poi点名称 | String | 必返回 | |
| | phone || 电话 | String | | |
| | address || 地址 | String | | |
| | lonlat || 坐标 | String | 必返回 | 坐标 x,y |
| | poiType || poi类型 | Int | 必返回 | 101:POI数据 102:公交站点 |
| | eaddress || 英文地址 | String | | |
| | ename || poi点英文名称 | String | | |
| | hotPointID || poi热点ID | String | 必返回 | 热点id |
| | province || 所属省名称 | String | | |
| | provinceCode || 省行政区编码 | String | | |
| | city || 所属城市名称 | String | | |
| | cityCode || 市行政区编码 | String | | |
| | county || 所属区县名称 | String | | |
| | countyCode || 区县行政区编码 | String | | |
| | source || 数据信息来源 | String | 必返回 | |
| | typeCode || 分类编码 | String | | |
| | typeName || 分类名称 | String | | |
| | stationData || 车站信息结构体 数据 | Json 数组 | | poiType=102 |
| | | lineName | 线路名称 | String | 必返回 | |
| | | uuid | 线路的id | String | 必返回 | |
| | | stationUuid | 公交站uuid | String | 必返回 | |
| statistics ||| 针对统计(类型2)集合返回 | Json 数组 | | resultType=2 |
| | count || 本次统计POI总数量 | Int | 必返回 | |
| | adminCount || 行政区数量 | Int | 必返回 | |
| | priorityCitys || 推荐行政区名称 | Json数组 | 必返回 | |
| | | name | 行政区名称 | String | 必返回 | |
| | | count | 城市数量 | Int | 必返回 | |
| | | lonlat | 行政区经纬度 | String | 必返回 | 坐标 x,y |
| | | ename | 英文行政名称 | String | 必返回 | |
| | | adminCode | 城市国标码 | Int | 必返回 | 9位国标码。 |
| | allAdmins || 各省包含信息集合 | Json数组 | 必返回 | |
| | | name | 行政名称 | String | 必返回 | |
| | | count | 包含数量 | Int | 必返回 | |
| | | lonlat | 行政区经纬度 | String | 必返回 | 坐标x,y |
| | | adminCode | 省国标码 | String | 必返回 | |
| | | ename | 英文行政名称 | String | 必返回 | |
| | | isleaf | 有无下一级行政区 | boolean | 必返回 | 有则false,无则true |
在官网的调用示例中,使用浏览器的调试工具来看一下数据返回的结果:

请特别注意以上的接口返回值,这里取其中的一个值对象信息如下:
javascript
{
"adminName": "浙江省",
"ename": "Zhejiang Province",
"count": 599616,
"adminCode": 156330000,
"isleaf": false,
"lonlat": "120.06772699999999,29.174674999999997"
}
这里返回的adminCode参数非常重要,在后面的层级调用中其实就是依赖这个adminCode来进行多层级实现的。
二、Leaflet多层级实现实例
本节将具体详述如何使用Leaflet来调用天地图的检索API和实现多层级调用,可以在Leafelt中实现我们自己的逻辑,可以展示省-市-POI详情等信息,最后给大家展示一些实际的效果。
1、层级调用实现原理
关于如何实现层级调用,在前面一节中也进行了简单说明。其实就是一个递归的调用,在进行API调用时,首先需要判断返回的数据类型是什么?如果从接口中返回的类型是1表示是poi数据,直接展示即可,如果是2表示是展示统计数据,再从统计数据中获取所有的信息,循环统计信息中的行政区划信息,然后再递归调用其行政区划的编码实现向下检索。实现的核心代码展示如下:
javascript
/**
* 调用天地图查询
*/
function callTdtSearch(keyWord,specify){
var queryUrl = "http://api.tianditu.gov.cn/v2/search?postStr={'keyWord':'"+ keyWord +"','queryType':12,'start':0,'count':10,'specify':'" + specify + "','show':'2'}&type=query&tk="+tdt_client_key;
$.ajax({
type: "get",
url:queryUrl,
data: {},
success: function(rsData) {
// 移除所有图层
myLayerGroup.clearLayers();
var rsObj = rsData;
var loc_info = rsObj.location;
var resultType = rsObj.resultType;
switch(resultType){
case 1 :
showPoi(rsObj);
break;
case 2:
showStatistics(rsObj);
break;
default:
console.log("不详");
}
map.addLayer(myLayerGroup);
}
});
}
2、Leaflet中多层级调用
根据接口中返回的类型不一样来进行个性化的展示,以此达到区别展示的效果。这里重点介绍如何来进行统计信息的展示和如何调用下一层级的POI信息,核心方法如下:
javascript
//点击还可以进行查询
function buildStatHtml(stat,index){
var html = "";
html += "<div class='marsBlackPanel' style='background:#ff9800;' animation-spaceInDown>";
html += "<div class='marsBlackPanel-text' style='' onclick='execQueryByCode("+stat.adminCode+")'>" + (index +1) + "、" +stat.adminName + "(" + stat.count + ")</div>";
html += "</div>";
return html;
}
function showStatistics(rsObj){
var statistics = rsObj.statistics.allAdmins;
for(var i = 0;i<statistics.length;i++){
var stat = statistics[i];
var lonlat = stat.lonlat;
var lonlatStr = lonlat.split(",");
var marker = L.marker([lonlatStr[1], lonlatStr[0]], {
icon: L.divIcon({
iconSize: null,
className: '',
popupAnchor:[5,5],
shadowAnchor:[5,5],
html: buildStatHtml(stat,i)
})
}).addTo(myLayerGroup);
}
map.fitBounds(myLayerGroup.getBounds());
}
这里实现的关键就是在自定义的标注中,绑定了一个执行查询的方法,然后再这个执行方法中又可以进行下一个层级的调用。 关键的方法如下:
javascript
function execQueryByCode(specify){
var keyWord = $("#address").val();
callTdtSearch(keyWord,specify);
}
这样就实现了层次的调用,当返回的值是具体的POI时,直接展示。反之则会进行省份的标注,同时可以点击当前省份,从而实现多层级的调用和展示。
3、成果展示
下面结合一些实际的例子来展示一下成果,具体是如何来进行相关的实现的。

在Web浏览器中输入请求地址,可以看到以上的界面效果。这样就基本模拟省份这个层级的统计展示。 需要说明的是,通过POI检索API的数据结果与官网的检索结果之前还是有一丢丢的区别,比如官网返回的结果是599616,而通过我们的接口调用,返回的结果是:596756;下面将结果整理一个表示,供大家参考,具体出现差异的原因可能需要咨询客服人员:
|----|-----|--------|--------|------|
| 序号 | 省份 | 官方返回结果 | 自主调用结果 | 差额 |
| 1 | 浙江省 | 599616 | 596756 | 2860 |
| 2 | 广东省 | 3307 | 3103 | 204 |
| 3 | 江苏省 | 2842 | 2519 | 323 |
| 4 | 江西省 | 2271 | 1918 | 353 |
| 5 | 湖南省 | 2230 | 1895 | 335 |
由此可以看出,搜索的结果还是存在一定的区别的。
下面来看一个有意思的地方,包含舒服两个字的最多的省份是湖北省。

而在湖北省中,武汉又是最多的,有41个地方包含舒服,

来看看具体包含舒服的地名都在武汉的哪些地方,

最后来看看天地图中包含火锅的最多的省份是哪里?

可以看到,出现火锅最多的地方居然是江苏省,在大家的印象里,江浙应该都是甜口居多。而喜好火锅的云贵川桔田前三都挤不进去,第四名为重庆,第六名四川。前三中,除江苏外,第二名是山东省,第三名是河南省,有没有出乎你的意料呢?

在江苏省中,苏州市的火锅又是地第一名的,有2028个, 如下:

确实基本上都是火锅了,这个确实没有想到,大苏州的火锅居然这么多。
三、总结
以上就是本文的主要内容,本文所记录的,正是这样一次"小而美"的实战:如何以 Leaflet 为壳、天地图为芯,逐级剖开中国行政体系的"套娃式"结构,把"模糊输入--多级联想--精准定位"做成一套可复用的前端组件。在WebGIS的可视化效果中,在省级节点中展示统计数量,随着地图的深入逐级进行细化,将地名逐渐呈现在大家的面前。本文不仅详细的介绍了如何在Leaflet中进行层级展示的实现,并且基于实际的地名进行了实际的检索展示,如果大家感兴趣,不妨来这里看看。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。