通過百度API獲取城市公交線路坐標點及站點信息


話不多說,先掛最后的數據結果,如果這是你想要的,我們再接着看:

公交線路坐標數據&公交站點坐標數據

    

 

 正文開始:

前期數據准備:獲取城市所有公交線路名稱

  使用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

 

前端小白一個,代碼寫得巨辣雞。如果能幫到你很高興,各位有興趣的老哥請多多批評指正,歡迎討論!!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM