通过百度API获取城市公交线路坐标点及站点信息

时间:2024-03-08 17:04:04

话不多说,先挂最后的数据结果,如果这是你想要的,我们再接着看:

公交线路坐标数据&公交站点坐标数据

    

 

 正文开始:

前期数据准备:获取城市所有公交线路名称

  使用python爬取,结果如下,代码参考:https://www.cnblogs.com/Qiuzhiyu/p/12183140.html 

 数据结果展示

 

需要准备的js包:

<!--用于坐标系转换的js包  详见github:https://github.com/hujiulong/gcoord  --> (非必须)
<script src="https://unpkg.com/gcoord/dist/gcoord.js"></script>
<!-- jquery -->
<script src=\'jquery-1.8.3.js\'></script>
<!-- 百度地图API -->
<script type="text/javascript" src="http://api.map.baidu.com/api?v=1.2"></script> <!-- js-xlsx包 使用以及获取方法见:https://www.cnblogs.com/liuxianan/p/js-excel.html --> (非必须,如果不需要导出数据到本地)
<script src =\'xlsx.full.min.js\'></script> <script src =\'xlsx.core.min.js\'></script> <script>

 

对爬取的数据进行处理:

 /*将爬取的广州公交信息进行转换*/
    var line_data_list = []
    var line_name_list = []   //获取公交线路名称

    $.ajax(
        {
            url:encodeURI(\'guangzhou.txt\'), //提前爬取的数据
            async: false,
            success: function (guangzhou) {
                row_split =  guangzhou.split(\'\n\')
                for(var i in row_split){
                    line_data_list.push(JSON.parse(row_split[i]))
                    var len =  JSON.parse(row_split[i]).线路名称[0].length
                    line_data_list[i].线路名称 = JSON.parse(row_split[i]).线路名称[0].substring(2,len-5) //’广州10路公交车路线‘->’10路‘
                }

                for(var i in line_data_list){
                    line_name_list.push(line_data_list[i].线路名称)
                }
            }

        }
    )

处理结果:

line_name_list =["10路", "11路", "12路", "14路", "15路", "19路", "101路", "102路",……]

接下来非常简单,只需要进行公交列表查询,这一步执行会激发后续一系列操作,获取我们想要的数据。

   // 数据导入数组开始
    //slice
    for (var i in line_name_list) {
        busline.getBusList(line_name_list[i]);
        console.log(i)
    }
    console.log("done")

 

但要搞懂发生了什么,还需要理解接下来的其他部分:

  首先,实例化百度地图,用于页面上的单个线路的展示,当然如果不想展示的话,这一块可以省略:

   /*获取百度地图实例*/
    var map = new BMap.Map("container");
    map.centerAndZoom(new BMap.Point(113.315224, 23.181452), 12); //广州市

  其次,创建百度地图公交信息获取类:

  在这一部分,我们设置了公交列表查询后的回调函数,公交列表查询后的回调函数中又进行了公交线路的查询,而公交线路的回调函数中进行了对公交线路数据的组织,这一系列操作最终使我们得到存储了公交线路信息的对象数组。

    // 公交信息获取类
    var busline = new BMap.BusLineSearch("广州",{
        // 展示获取的公交线路,自动生成面板到id=results的element上,不必须使用
        renderOptions:{map:map,panel:"results"},
        // 设置公交列表查询后的回调函数,注意与公交线路查询区分:
     //公交列表存储多个具体的公交对象,公交对象可理解为公交线路。
onGetBusListComplete: function(result){ if(result) {
          
/*获取查询出的公交列表中的对象 0代表上行 1代表下行,实际上还可能存在getBusListItem(2)、getBusListItem(3)等等, 代表的是模糊查询的结果,例如查询 10路 时,getBusListItem(0)、getBusListItem(1)返回的是10路的上下行的公交线路, 而getBusListItem(2)、getBusListItem(3)返回的是b10路,具体可参考代码块后的贴图*/
          let up_Line
= result.getBusListItem(0); //获取查询出的公交列表中的第一个对象,即上行线路 let down_Line = result.getBusListItem(1); //获取查询出的公交列表中的第一个对象,即下行线路 if(typeof up_Line === "object"){ //判断查询结果是否存在 busline.getBusLine(up_Line); //执行公交线路查询 }else{ console.log("查无此公交:"+result.keyword) //在控制台输出无法查询的线路名称,result.keyword 即在查询时输入的线路名 } if(typeof down_Line === "object"){ busline.getBusLine(down_Line); }else{ console.log("此公交无下行或不存在:"+result.keyword) //部分公交线路不存在下行线路,输出观察 } } }, //设置公交线路查询后的回调函数,即执行 busline.getBusLine(up_Line) 后执行的函数 onGetBusLineComplete : function (ret) { let line_name = ret.name; let line_point = ret.getPath(); //获取公交路线坐标 let i = ret.getNumBusStations(); let sta_info = []; for(j=0;j <= i;j++){ sta_info.push([ret.getBusStation(j)]); } get_bus_line_data(line_name,line_point,sta_info); //对数据进行组织 } });

 路线与公交线路列表展示:

up_line数据结构示例,即公交列表的第一个对象:

线路查询后的回调函数的参数数据结构:

 

 上一代码块最后一行代码的函数,会对线路查询后的回调函数的参数(即上图公交线路信息)存入目标数组:

    function get_bus_line_data(line_name,line_point,sta_info) {

        var sta_info_,sta_name,sta_position;
        //更改数据结构  [{}] --> {}  && 去除最后一个空数组
        sta_info_ = sta_info.slice(0,sta_info.length - 1).map(function (re) {
            return re[0]
        });
        //获取每一站点名称
        sta_name = sta_info_.map(function (re) {
            return re.name.toString()
        });
        // 获取站点坐标
        sta_position = sta_info_.map(function (re) {
            return fromBd09ToWgs84([re.position.lng,re.position.lat])
            // return [re.position.lng,re.position.lat]  若不需要转换坐标系,使用此行代码
        });

        line_data_ass.push(
            {
                name: line_name,
                line_point: line_point.map(function (re) {
                    return fromBd09ToWgs84([re.lng, re.lat])
                    // return fromBd09ToWgs84([re.lng, re.lat]) 若不需要转换坐标系,使用此行代码
                }),
                sta_name : sta_name,
                sta_point : sta_position,
            });
        console.log(line_name);  //在控制台输出成功获取的公交线路信息的名称
    }

输出的line_data_ass格式形式如图:

   这是一个对象数组,每个对象存储了一条路线的所有信息,包括线路坐标点,站点名称,站点坐标,已经包含了我们需要的所有信息。可以看到,上下行线路在名称上是有区别的。

 

如果只需要这一对象数组,上述代码足矣,完整代码整理如下:

<!DOCTYPE html>

<html>
<head>
    <title>获取公交信息</title>
</head>
<body>

<p><img src="http://map.baidu.com/img/logo-map.gif" /><span style="display:inline-block;width:200px;">&nbsp;</span><input type="text" value="331" id="busId" />路公交&nbsp;<input type="button" value="查询" onclick="busSearch();" /></p>


<div style="float:left;width:600px;height:500px;border:1px solid gray" id="container"></div>

<div id="results" style="float:left;width:300px;height:500px;font-size:13px;"></div>

<!--获取坐标系转换js包    详见github:https://github.com/hujiulong/gcoord  -->
<script src="https://unpkg.com/gcoord/dist/gcoord.js"></script>
<!--本地加载jquery-->
<script src=\'jquery-1.8.3.js\'></script>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=1.2"></script>
<!--本地加载js-xlsx包  使用以及获取方法见:https://www.cnblogs.com/liuxianan/p/js-excel.html-->
<script src =\'xlsx.full.min.js\'></script>
<script src =\'xlsx.core.min.js\'></script>
<script>




    /*将爬取的广州公交信息进行转换*/
    var line_data_list = []
    var line_name_list = []   //获取公交线路名称


    $.ajax(
        {
            url:encodeURI(\'guangzhou.txt\'),
            async: false,
            success: function (guangzhou) {
                row_split =  guangzhou.split(\'\n\')
                for(var i in row_split){
                    line_data_list.push(JSON.parse(row_split[i]))
                    var len =  JSON.parse(row_split[i]).线路名称[0].length
                    line_data_list[i].线路名称 = JSON.parse(row_split[i]).线路名称[0].substring(2,len-5)
                    //’广州10路公交车路线‘->’10路‘
                }

                for(var i in line_data_list){
                    line_name_list.push(line_data_list[i].线路名称)
                }
            }

        }
    )



    /*获取百度地图实例*/
    var map = new BMap.Map("container");
    map.centerAndZoom(new BMap.Point(113.315224, 23.181452), 12); //广州市

    var line_data_ass = []  //存储最终数据的集合





    function get_bus_line_data(line_name,line_point,sta_info) {

        var sta_info_,sta_name,sta_position;
        //更改数据结构  [{}] --> {}  && 去除最后一个空数组
        sta_info_ = sta_info.slice(0,sta_info.length - 1).map(function (re) {
            return re[0]
        });
        //获取每一站点名称
        sta_name = sta_info_.map(function (re) {
            return re.name.toString()
        });
        // 获取站点坐标
        sta_position = sta_info_.map(function (re) {
            return fromBd09ToWgs84([re.position.lng,re.position.lat])
            // return [re.position.lng,re.position.lat]  若不需要转换坐标系,使用此行代码
        });

        line_data_ass.push(
            {
                name: line_name,
                line_point: line_point.map(function (re) {
                    return fromBd09ToWgs84([re.lng, re.lat])
                    // return fromBd09ToWgs84([re.lng, re.lat]) 若不需要转换坐标系,使用此行代码
                }),
                sta_name : sta_name,
                sta_point : sta_position,
            });
        console.log(line_name);  //在控制台输出成功获取的公交线路信息的名称
    }




    // 公交信息获取类
    var busline = new BMap.BusLineSearch("广州",{
        // 展示获取的公交线路,自动生成面板到id=results的element上,不必须使用
        renderOptions:{map:map,panel:"results"},
        // 设置公交列表查询后的回调函数,注意与公交线路查询区分,公交列表存储多个具体的公交对象,公交对象可理解为公交线路。
        onGetBusListComplete: function(result){
            if(result) {
                /*获取查询出的公交列表中的对象 0代表上行 1代表下行,
                实际上还可能存在getBusListItem(2)、getBusListItem(3)等等,代表的是模糊查询的结果,
                例如查询 10路时,getBusListItem(0)、(1)返回的是10路的上下行的公交线路,
                而(2)(3)返回的是b10路,具体可参考代码块后的贴图*/

                //获取查询出的公交列表中的第一个对象,即下行线路
                let up_Line = result.getBusListItem(0);
                //获取查询出的公交列表中的第一个对象,即下行线路
                let down_Line = result.getBusListItem(1); 
                if(typeof up_Line === "object"){  //判断查询结果是否存在
                    busline.getBusLine(up_Line);  //执行公交线路查询
                }else{
                    //在控制台输出无法查询的线路名称,result.keyword 即在查询时输入的线路名
                    console.log("查无此公交:"+result.keyword) 
                }
                if(typeof down_Line === "object"){
                    busline.getBusLine(down_Line);
                }else{
                    //部分公交线路不存在下行线路,输出观察
                    console.log("此公交无下行或不存在:"+result.keyword) 
                }
            }
        },
        //设置公交线路查询后的回调函数,即执行 busline.getBusLine(up_Line) 后执行的函数
        onGetBusLineComplete : function (ret) {
            let line_name = ret.name;
            let line_point = ret.getPath();  //获取公交路线坐标
            let i = ret.getNumBusStations();
            let sta_info = [];
            for(j=0;j <= i;j++){
                sta_info.push([ret.getBusStation(j)]);
            }
            get_bus_line_data(line_name,line_point,sta_info); //对数据进行组织
        }
    });

    /*------------------执行层------------------------------------*/

    // 数据导入数组开始
    //slice
    for (var i in line_name_list.slice(0,5)){
        busline.getBusList(line_name_list[i]);
        console.log(i)
    }
    console.log("done")



    /*-------------------功能函数---------------------------------*/

    /*页面操作调用的函数*/
    function busSearch(){
        var busName = document.getElementById("busId").value;
        busline.getBusList(busName);
    }


    /*坐标转换函数*/
    function fromBd09ToWgs84(arr) {
        var result = gcoord.transform(
            arr,    // 经纬度坐标
            gcoord.BD09,                 // 当前坐标系
            gcoord.WGS84                   // 目标坐标系
        );
        return result;
    }


</script>
</body>
</html>

 

 如何导出?

  很多情况下,如果只是把数据存储到JavaScript的一个数组中,是远远不够的。接下来将会提供将这些数据导出到本地excel的方式,最终获取在开头展示的数据文件。

  接下来的步骤会比较恶心了,因为通过百度API获取公交数据需要一定的时间,所以如果在页面加载过程中就认为已获取了所有所需的数据,直接进行导出或者解析的话,程序会报错。因为在页面执行到提取数据的代码时,往往我们所需的数据还没有完全传送过来,特别是在一个城市有数千条公交线路的时候。

  对于这一问题,解决方案是:在数据完全获取后,即都已存入line_data_ass之后,再F12调出控制台执行导出数据的JavaScript代码。

在控制台执行代码之前,还需要下列准备:

    /*  
    ①为了配合js-xlsx包导出数据,对数据进行进一步的组织。
    ②准备必要的导出函数。
    */

// 数据组织 data_line_point=[[\'line_id\',\'line_name\',\'lng\',\'lat\',\'p_id\']]; data_station_point = [\'line_id\',\'line_name\',\'sta_name\',\'lng\',\'lat\',\'sta_id\']; function createData(){ for(var i in line_data_ass){ for(var j in line_data_ass[i].line_point){ data_line_point.push([ i, line_data_ass[i].name, line_data_ass[i].line_point[j][0], line_data_ass[i].line_point[j][1], j // , line_data_ass[i].dir ])} for(var j in line_data_ass[i].sta_point){ data_station_point.push([ i, line_data_ass[i].name, line_data_ass[i].sta_name[j], line_data_ass[i].sta_point[j][0], line_data_ass[i].sta_point[j][1], j // , line_data_ass[i].dir ]); } } } /*基于excel_js的函数*/ //代码来源:https://www.cnblogs.com/liuxianan/p/js-excel.html function openDownloadDialog(url, saveName) { if(typeof url == \'object\' && url instanceof Blob) { url = URL.createObjectURL(url); // 创建blob地址 } var aLink = document.createElement(\'a\'); aLink.href = url; aLink.download = saveName || \'\'; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效 var event; if(window.MouseEvent) event = new MouseEvent(\'click\'); else { event = document.createEvent(\'MouseEvents\'); event.initMouseEvent(\'click\', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); } aLink.dispatchEvent(event); } function sheet2blob(sheet, sheetName) { sheetName = sheetName || \'sheet1\'; var workbook = { SheetNames: [sheetName], Sheets: {} }; workbook.Sheets[sheetName] = sheet; // 生成excel的配置项 var wopts = { bookType: \'xlsx\', // 要生成的文件类型 bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性 type: \'binary\' }; var wbout = XLSX.write(workbook, wopts); var blob = new Blob([s2ab(wbout)], {type:"application/octet-stream"}); // 字符串转ArrayBuffer function s2ab(s) { var buf = new ArrayBuffer(s.length); var view = new Uint8Array(buf); for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; return buf; } return blob; }

 

等所有数据载入到line_data_ass中后,在控制台执行以下代码:

   //执行数据组织函数
    createData();
    //导出excel
    var sheet = XLSX.utils.aoa_to_sheet(data_line_point);
    var sheet2 = XLSX.utils.aoa_to_sheet(data_station_point);
    openDownloadDialog(sheet2blob(sheet), \'导出.xlsx\');
    openDownloadDialog(sheet2blob(sheet2), \'导出1.xlsx\');

   执行完后,不出意外数据就可以成功导出了。

 

不得不提的话:

如果你按着步骤运行这一程序,同时想要提取的公交线路又很多的时候,可能会出现以下几个问题:

1.针对以下问题,可能的原因是:可以通过百度地图API获取到相应的busLine对象,但却没有相应的数据,例如查询广州的"从化19路密石班车",就会出现这样的情况。

2.单独查找时存在的公交线路,在程序运行过程中却报出:"查无此公交:XXX"。这一问题目前还没有很好的解决方案,不知道是哪里出了BUG,一个解决思路是:一次性请求较少量的公交线路数据。这样会比较少出错。

3.查找的公交线路不存在,但百度地图的模糊查找会找出名称相识的路线,例如,输入”化16路“,得到的是”16路“。这一问题尝试过通过字符串的indexOf方法判断,代码如下。但这又会导致输入”12A路”时,“12a路”的数据无法获取。不过好像百度地图公交线路的名称中英文字符多是小写,所以把输入中的英文统一改为小写或许可以解决这个问题。有点麻烦,而且不一定有用,另外这一错误最终导致的后果只是导出的数据中多了一条重复线路,无伤大雅,后期也不难处理,所以就算了。

if(typeof up_Line === "object" && up_Line.name.indexOf(result.keyword) !== -1){...}z

参考资料 :

百度js3.0 API文档:http://lbsyun.baidu.com/cms/jsapi/reference/jsapi_reference_3_0.html

小茗同学(如何使用JavaScript实现纯前端读取和导出excel文件):https://www.cnblogs.com/liuxianan/p/js-excel.html

求知鱼(爬取某城市公交钱路--xpath过滤):https://www.cnblogs.com/Qiuzhiyu/p/12183140.html

酸奶小妹(【百度地图API】如何制作公交线路的搜索?如331路):https://www.cnblogs.com/milkmap/archive/2011/09/16/2178553.html

 

前端小白一个,代码写得巨辣鸡。如果能帮到你很高兴,各位有兴趣的老哥请多多批评指正,欢迎讨论!!!!