作为数学苦手,每次参加数模(虽然加上这次也就两次)都是抱着划水抱大腿的打算参加的……C题要求的是搜集某城市航班和该城市机场出租相关信息,航班信息倒简单,爬一下携程、飞常准之类的网站就行了。机场出租信息真是要了老命……按思路有两种,一种是查机场出租车秩序站的相关接口,在某乎上有看到有匿名网友爬到了郑州新郑机场出租车秩序站的一个网页,但找来找去没找着;第二种则是统计筛选某城市所有出租车的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
编号 | 时间 | 经度 | 纬度 | 速度 | 偏移角度 | 载客状态 |
---|---|---|---|---|---|---|
2108 | 2007-02-20 00:01:06 | 121.438500 | 31.249000 | 0 | 45 | 0 |
实现过程
整个数据有4000多份,每一份都是一辆出租车一天约每隔一分钟产生的位置信息。因为其中不乏没有出动的、只在市区载客的出租车,我们需要筛选出经过上海浦东机场的数据,从百度地图上找出上海浦东机场的位置为[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条数据经过了浦东机场,还是比较可观的。
随后就开始前端展示的搭建,我的需求是网页里可以打开相对应的文本,随后绘制出一天中所有的路径,其中载客状态由不同颜色的线条表示。同时有一个可拖动的进度条,可以控制一天内的大致时间来观察其路径的具体方向。
首先是一个打开文件的按钮,为其绑定事件。
<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需要传入一个经纬度的数组和线条的样式。只不过需要注意的是,由于我想要路径颜色根据载客状态不同而不同,所以需要多段绘制。一开始图方便每两个点就进行一次绘图,但1700多个经纬度点就要绘图1700多次,导致网页非常卡顿。后来优化了一下,修改成每一段载客/空载进行一次绘图。
拖动条的样式和代码来自网络,具体也只是修改相应的回调函数,可参考代码。
完整代码
注:感谢网络上分享各组件代码的网友,同时因为是建模过程中写的,写的很忙乱,很多地方写的非常不美观,仅供学习参考。
<!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.cnzz.com/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"); // 创建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>
效果演示
12
如图便为一个在机场排队等待载客的典型选择,其在6时左右载客到达机场,并等待排队约三小时后载客再度离开机场。
3 4
如图为载客至机场后放弃排队直接返回的典型选择,司机在9时50分左右载客至机场后立刻空载返回,并于10时40分左右在市区周围再次揽客载至其他地点。
网友评论