Get city bus lines coordinate points and site information through Baidu API

Man of few words said before hanging the final data results, if this is what you want, and then we'll see:

Bus lines coordinate data & bus stations coordinate data

    

 

 

 

 

 

 Text began:

Preliminary data preparation: Get all city bus lines Name

Python crawling using, as a result, the code reference: https: //www.cnblogs.com/Qiuzhiyu/p/12183140.html 

 数据结果展示

 

 

Js need to prepare the package:

<!--用于坐标系转换的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

 

 

 

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

 

Guess you like

Origin www.cnblogs.com/console-chan/p/12399020.html