作為數學苦手,每次參加數模(雖然加上這次也就兩次)都是抱著劃水抱大腿的打算參加的……C題要求的是搜集某城市航班和該城市機場出租相關信息敞葛,航班信息倒簡單腺占,爬一下攜程、飛常準之類的網站就行了妙黍。機場出租信息真是要了老命……按思路有兩種,一種是查機場出租車秩序站的相關接口楣导,在某乎上有看到有匿名網友爬到了鄭州新鄭機場出租車秩序站的一個網頁废境,但找來找去沒找著;第二種則是統(tǒng)計篩選某城市所有出租車的GPS路徑筒繁,本來覺得這數據處理也太麻煩了噩凹,也找不到數據。在幾乎打算直接編造數據的時候毡咏,在某乎上找到了一份2007年上海出租車的GPS數據驮宴,處理了一下也還好。做了個簡單的可視化呕缭,雖然對論文估計沒啥用堵泽,但還是挺有意思的修己,這里記錄一下簡單的js和相關接口的運用,萌新如我也可以輕松學會迎罗。
數據格式
一段示例數據:
2108,2007-02-20 00:01:06,121.438500,31.249000, 0, 45,0
2108,2007-02-20 00:02:07,121.438500,31.249000, 0, 45,0
2108,2007-02-20 00:03:08,121.438500,31.249000, 0, 45,0
2108,2007-02-20 00:04:09,121.438500,31.249000, 0, 45,0
編號 | 時間 | 經度 | 緯度 | 速度 | 偏移角度 | 載客狀態(tài) |
---|---|---|---|---|---|---|
2108 | 2007-02-20 00:01:06 | 121.438500 | 31.249000 | 0 | 45 | 0 |
實現過程
整個數據有4000多份睬愤,每一份都是一輛出租車一天約每隔一分鐘產生的位置信息。因為其中不乏沒有出動的纹安、只在市區(qū)載客的出租車尤辱,我們需要篩選出經過上海浦東機場的數據,從百度地圖上找出上海浦東機場的位置為[121.81509,31.157478]厢岂,為了方便簡單的定義經過該坐標半徑3000m即視作經過浦東機場光督。
# -*- coding:utf-8 -*-
import os
from math import radians, cos, sin, asin, sqrt
import shutil
#計算兩點間距離-m
def geodistance(lng1,lat1,lng2,lat2):
lng1, lat1, lng2, lat2 = map(radians, [lng1, lat1, lng2, lat2])
dlon=lng2-lng1
dlat=lat2-lat1
a=sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
dis=2*asin(sqrt(a))*6371*1000
return dis
def all_path(dirname):
result = []#所有的文件
for maindir, subdir, file_name_list in os.walk(dirname):
for filename in file_name_list:
apath = os.path.join(maindir, filename)#合并成一個完整路徑
result.append(apath)
return result
if __name__ == '__main__':
dis = [121.81509,31.157478]
path = r'E:\雜圖作品\20190912數學建模\數據(打開前請看備注)'
to_path = r'E:\雜圖作品\20190912數學建模\經過浦東機場的數據'
txts = all_path(path)
count = 0;
for i in txts:
f = open(i)
for s in f.readlines():
a = s.split(',')
if(geodistance(float(a[2]),float(a[3]),dis[0],dis[1])<3000):
print("%s經過了浦東機場"%i)
shutil.copy(i, to_path)
count+=1
break
print("共%d輛出租車經過浦東機場"%count)
經過篩選,有263條數據經過了浦東機場塔粒,還是比較可觀的结借。
隨后就開始前端展示的搭建,我的需求是網頁里可以打開相對應的文本卒茬,隨后繪制出一天中所有的路徑船老,其中載客狀態(tài)由不同顏色的線條表示。同時有一個可拖動的進度條圃酵,可以控制一天內的大致時間來觀察其路徑的具體方向努隙。
首先是一個打開文件的按鈕,為其綁定事件。
<input id='file' type="file" onchange="upload(this)" />
function upload(input) {
//支持chrome IE10
if (window.FileReader) {
var file = input.files[0];
console.log(file)
filename = file.name.split(".")[0];
var reader = new FileReader();
reader.onload = function() {
var arr = this.result.split("\n");
arrData = arr;
console.log(arr);
map.clearOverlays();
drawLines(arr);
}
// reader.readAsText(file,"UTF-8");
reader.readAsText(file,"gb2312");
}
//支持IE 7 8 9 10
else if (typeof window.ActiveXObject != 'undefined'){
var xmlDoc;
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.load(input.value);
alert(xmlDoc.xml);
}
//支持FF
else if (document.implementation && document.implementation.createDocument) {
var xmlDoc;
xmlDoc = document.implementation.createDocument("", "", null);
xmlDoc.async = false;
xmlDoc.load(input.value);
alert(xmlDoc.xml);
} else {
alert('error');
}
因為我使用的是chrome瀏覽器辜昵,所以只在其中一個分支里進行了邏輯處理,其他的兼容性分支也是類似的咽斧。
reader.onload = function() {
var arr = this.result.split("\n");
arrData = arr;
console.log(arr);
map.clearOverlays();
drawLines(arr);
}
即載入文件后堪置,就對百度地圖的組件進行重繪,具體百度地圖API的接入可參考代碼或者百度的文檔张惹。
function drawLines(arr,p){
p = p || 1.0;
var t = arr[0].split(",");
var lastLong = t[2];
var lastLat = t[3];
var isFull = 0;
var lastFull = 0;
var temp = [];
for(var i = 0 ; i < arr.length*p - 1; i=i+1){
var a = arr[i].split(",");
if(a[2]<120||a[2]>122||a[3]<30||a[3]>33)
continue;
if(a[6]!=lastFull || i == arr.length - 2){
var c = "green";
if (lastFull==1){
c = "red";
}
var polyline = new BMap.Polyline(temp, {strokeColor:c,
strokeWeight:3,
strokeOpacity:1});
map.addOverlay(polyline);
temp = [];
temp.push(new BMap.Point(lastLong,lastLat))
}
temp.push(new BMap.Point(a[2],a[3]));
lastLong = a[2];
lastLat = a[3];
lastFull = a[6];
}
}
邏輯也比較簡單舀锨,對每行文本進行經緯度提取然后再通過BMap.Polyline()進行繪圖,該API需要傳入一個經緯度的數組和線條的樣式宛逗。只不過需要注意的是坎匿,由于我想要路徑顏色根據載客狀態(tài)不同而不同,所以需要多段繪制雷激。一開始圖方便每兩個點就進行一次繪圖替蔬,但1700多個經緯度點就要繪圖1700多次,導致網頁非呈合荆卡頓承桥。后來優(yōu)化了一下,修改成每一段載客/空載進行一次繪圖根悼。
拖動條的樣式和代碼來自網絡凶异,具體也只是修改相應的回調函數蜀撑,可參考代碼。
完整代碼
注:感謝網絡上分享各組件代碼的網友剩彬,同時因為是建模過程中寫的酷麦,寫的很忙亂,很多地方寫的非常不美觀喉恋,僅供學習參考沃饶。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微軟雅黑";}
</style>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=2.0&ak=你的百度APIak碼"></script>
<title>車輛運行軌跡測試</title>
<script src="http://c.#/core.php"></script>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<style>
*{
margin: 0;
padding: 0;
}
.scroll{
width: 400px;
height: 8px;
background-color: #ccc;
margin: 10px;
position: relative;
}
.bar{
width: 10px;
height: 22px;
background-color: #369;
position: absolute;
top: -7px;
left: 0;
cursor: pointer;
}
.mask{
width: 0;
height: 100%;
background-color: #336699;
/*position: absolute;
top: 0;
left: 0;*/
}
</style>
</head>
<body>
<input id='file' type="file" onchange="upload(this)" />
<div class="scroll" id="scrollBar">
<div class="bar"></div>
<div class="mask"></div>
</div>
<div class="demo" id="demo"></div>
<div id="allmap"></div>
</body>
</html>
<script type="text/javascript">
// 獲取元素
var scrollBar = document.getElementById("scrollBar");
var bar = scrollBar.children[0];
var mask = scrollBar.children[1];
var demo = document.getElementById("demo");
// 拖動原理
bar.onmousedown = function(event){
var event = event || window.event;
var leftVal = event.clientX - this.offsetLeft;
// 拖動放到down的里面
var that = this;
var p = 0.0;
document.onmousemove = function(event){
var event = event || window.event;
that.style.left = event.clientX - leftVal + "px";
// 限制條件
var val = parseInt(that.style.left);
if(val < 0){
that.style.left = 0;
}else if(val > 390){
that.style.left = "390px";
}
// 移動的距離為遮罩的寬度
mask.style.width = that.style.left;
// 顯示百分比
p = parseInt(that.style.left) / 390;
var hour = parseInt(p*24);
var min = 24*60*p - hour*60;
demo.innerHTML = "移動了:"+ parseInt(parseInt(that.style.left) / 390 * 100) + "%" +"約"+hour+":"+min;
// 清除拖動 --- 防止鼠標已經彈起時還在拖動
window.getSelection ? window.getSelection().removeAllRanges():document.selection.empty();
map.clearOverlays();
drawLines(arrData,p);
}
// 鼠標抬起停止拖動
document.onmouseup = function(){
document.onmousemove = null;
//清空畫布
map.clearOverlays();
console.log("p:"+p)
//根據比例繪圖
drawLines(arrData,p);
}
}
var arrData = [];
var linesPoints = null;
// 百度地圖API功能
var map = new BMap.Map("allmap"); // 創(chuàng)建Map實例
map.centerAndZoom(new BMap.Point(121.487500,31.374500), 15); // 初始化地圖,設置中心點坐標和地圖級別
map.addControl(new BMap.MapTypeControl()); //添加地圖類型控件
map.setCurrentCity("上海"); // 設置地圖顯示的城市 此項是必須設置的
map.enableScrollWheelZoom(true); //開啟鼠標滾輪縮放
function drawGreenLine(startLong,startLat,endLong,endLat){
var polyline = new BMap.Polyline([
new BMap.Point(startLong,startLat),//起始點的經緯度
new BMap.Point(endLong,endLat)//終止點的經緯度
], {strokeColor:"green",//設置顏色
strokeWeight:3, //寬度
strokeOpacity:1});//透明度
map.addOverlay(polyline);
}
function drawRedLine(){
startLongR = endLongR;
startLatR = endLatR;
var polyline1 = new BMap.Polyline([
new BMap.Point(startLongR,startLatR),//起始點的經緯度
new BMap.Point(endLongR,endLatR)//終止點的經緯度
], {strokeColor:"red",//設置顏色
strokeWeight:3, //寬度
strokeOpacity:1});//透明度
map.addOverlay(polyline1);
}
function drawLines(arr,p){
p = p || 1.0;
var t = arr[0].split(",");
var lastLong = t[2];
var lastLat = t[3];
var isFull = 0;
var lastFull = 0;
var temp = [];
for(var i = 0 ; i < arr.length*p - 1; i=i+1){
var a = arr[i].split(",");
if(a[2]<120||a[2]>122||a[3]<30||a[3]>33)
continue;
if(a[6]!=lastFull || i == arr.length - 2){
var c = "green";
if (lastFull==1){
c = "red";
}
var polyline = new BMap.Polyline(temp, {strokeColor:c,
strokeWeight:3,
strokeOpacity:1});
map.addOverlay(polyline);
temp = [];
temp.push(new BMap.Point(lastLong,lastLat))
}
temp.push(new BMap.Point(a[2],a[3]));
lastLong = a[2];
lastLat = a[3];
lastFull = a[6];
}
}
function upload(input) {
//支持chrome IE10
if (window.FileReader) {
var file = input.files[0];
console.log(file)
filename = file.name.split(".")[0];
var reader = new FileReader();
reader.onload = function() {
var arr = this.result.split("\n");
arrData = arr;
console.log(arr);
map.clearOverlays();
drawLines(arr);
}
// reader.readAsText(file,"UTF-8");
reader.readAsText(file,"gb2312");
}
//支持IE 7 8 9 10
else if (typeof window.ActiveXObject != 'undefined'){
var xmlDoc;
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.load(input.value);
alert(xmlDoc.xml);
}
//支持FF
else if (document.implementation && document.implementation.createDocument) {
var xmlDoc;
xmlDoc = document.implementation.createDocument("", "", null);
xmlDoc.async = false;
xmlDoc.load(input.value);
alert(xmlDoc.xml);
} else {
alert('error');
}
}
</script>
效果演示
如圖便為一個在機場排隊等待載客的典型選擇,其在6時左右載客到達機場瀑晒,并等待排隊約三小時后載客再度離開機場绍坝。
如圖為載客至機場后放棄排隊直接返回的典型選擇,司機在9時50分左右載客至機場后立刻空載返回苔悦,并于10時40分左右在市區(qū)周圍再次攬客載至其他地點轩褐。