Python和Perl绘制中国北京跑步地图的方法


Posted in Python onMarch 03, 2016

当你在一个城市,穿越大街小巷,跑步跑了几千公里之后,一个显而易见的想法是,我到底和之前比快了多少,跑量有何变化,如果能把在这个城市的所有路线全部画出来,会是怎样的景象呢?

1.数据来源:益动GPS

文章代码比较多,为了不吊人胃口,先看看最终效果:

Python和Perl绘制中国北京跑步地图的方法[/code]

首先需要有原始数据信息,手机上众多跑步软件提供了详细的记录,但它们共同的问题是不允许自由导入导出(可能是为了用户粘性吧)。因此有一块智能运动手表应该是不二之选。我的是Garmin Fenix3,推荐一下:

Python和Perl绘制中国北京跑步地图的方法

益动GPS算是业界良心了,能够同步咕咚,Garmin手表,悦跑圈的数据,因此我将其作为一个入口,抓取所有的GPS数据。

至于如何同步,可参考网站上的相关介绍,下面是我登录该网站后的截图:

http://edooon.com/user/5699607196/record/15414378

Python和Perl绘制中国北京跑步地图的方法

随便点进去以后,就可以看到导出路线的按钮:

Python和Perl绘制中国北京跑步地图的方法

无比坑爹的是,它不提供批量导出的按钮,几百条记录,依次导出都累死了。于是考虑用代码来编辑吧。

2. 获取益动网站上的数据

登录之后,可以看出它是动态加载,当滚轮滚到最下时,自动加载后面的内容。本来是应该嗅探和分析http请求的,后来懒惰了。当拖到底,全部加载完毕后,保存了当前的html文件。

接下来就是解析这个Html,基本上是通过XPath的来做的。有经验的同学看了下图就都明白了:

Python和Perl绘制中国北京跑步地图的方法

图中高亮的部分,就是要下载gpx文件的实际地址。我们将其保存在urllist中。同时,元数据被保存在json文件里。

folder = u'D:/buptzym的同步盘/百度云/我的文档/数据分析/datasets/rungps/';
cookie='JSESSIONID=69DF607B71B1F14AFEC090F520B14B55; logincookie=5699607196$6098898D08E533587E82B33DD9D02196; persistent_cookie=5699607196$42C885AD38F59DCA407E09C95BE1A60B; uname_forloginform="buptzym@qq.com"; __utma=54733311.82935663.1447906150.1447937410.1456907433.7; __utmb=54733311.5.10.1456907433; __utmc=54733311; __utmz=54733311.1456907433.7.3.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; cookie_site=auto'
userid='5699607196';
f = codecs.open(folder + 'desert.htm', 'r', 'utf-8');
html = f.read();
f.close();
root = etree.HTML(html)
tree = etree.ElementTree(root);
listnode=tree.xpath('//*[@id="feedList"]');
numre=re.compile(u'骑行|跑步|公里|,|耗时|消耗|大卡');
urllists=[]
records=[];
for child in listnode[0].iterchildren():
record={};
temp=child.xpath('div[2]/div[1]/a[2]')
if len(temp)==0:
continue;
source= temp[0].attrib['href'];
record['id']=source.split('/')[-1];
info=temp[0].text;
numinfo= numre.split(info);
if len(numinfo)<6:
continue;
record['type']= info[0:2];
record['distance']= numinfo[1];
record['hot']=numinfo[6];
urllists.append('http://edooon.com/user/%s/record/export?type=gpx&id=%s' % (userid, record['id']));

值得注意的是,因为下载时需要cookie,因此读者需要将自己在益动GPS的userid和登录的cookie都替换掉。

接下来就是下载的过程,获取导出数据按钮的URL的XPath,构造一个带cookie的请求,然后保存文件即可,非常容易。

opener = urllib.request.build_opener()
opener.addheaders.append(('Cookie', cookie));
path='//*[@id="exportList"]/li[1]/a';
for everyURL in urllists:
id = everyURL.split('=')[-1];
print(id);
url='http://edooon.com/user/%s/record/%s' % (userid, id);
f = opener.open(url);
html = f.read();
f.close();
root = etree.HTML(html)
tree = etree.ElementTree(root);
fs = str(tree.xpath(path)[0]);
if fs is None:
continue;
furl = 'http://edooon.com/user/%s/record/%s' % (userid, fs);
f = opener.open(furl);
html = f.read();
f.close();
filename=folder+'id'+'.gpx';
xmlfile = codecs.open(filename, 'wb');
xmlfile.write(html);
xmlfile.close();

之后,我们便保存了大约300多个gpx文件

3. 解析gpx数据

所谓gpx数据,是一种通用规范的GPS数据格式,详细的资料可自行搜索。

我们需要使用python的gpx解析器, gpxpy是个好选择,使用

pip3 install gpxpy 即可安装。

gpxpy提供了丰富的接口,当然为了统计,我们只需要提取一部分数据:

def readgpx(x):
file= open(dir+x+'.gpx','r')
txt=file.read()
gpx=gpxpy.parse(txt)
mv=gpx.get_moving_data()
dat= {'移动时间':mv.moving_time,'静止时间':mv.stopped_time,'移动距离':mv.moving_distance,'暂停距离':mv.stopped_distance,'最大速度':mv.max_speed};
dat['总时间']=(gpx.get_duration())
dat['id']=str(x)
updown=gpx.get_uphill_downhill()
dat['上山']=(updown.uphill);
dat['下山']=(updown.downhill)
timebound=gpx.get_time_bounds();
dat['开始时间']=(timebound.start_time)
dat['结束时间']=(timebound.end_time)
p=gpx.get_points_data()[0]
dat['lat']=p.point.latitude
dat['lng']=p.point.longitude
file.close()
return dat

readgpx函数会读取文件名x,并将一个字典返回。并得到类似下面的一张表:

Python和Perl绘制中国北京跑步地图的方法

因为我们只需要绘制北京的区域,因此需要一个坐标表达式筛掉北京之外的地区。筛选代码使用了pandas,在附件里有更详细的代码。

exceptids=详细[(详细.lng<116.1)|(详细.lng>116.7)|(详细.lat<39.9)|(详细.lat>40.1)].id

def filtercity(r):
sp=r.split('/')[-1].split('.')
if sp[1]!='gpx':
return False;
if sp[0] in exceptids.values:
return False;
return True; 
bjids= [r for r in gpxs if filtercity(r)]

这样,我们就将所有在北京完成的运动数据筛选了出来。

4.绘制GPS数据

反复造轮子是不好玩的,绘制gpx已经有比较强大的库,地址在http://avtanski.net/projects/gps/

很不幸,这个库使用Perl作为开发语言,并使用了GD作为视觉渲染库。我花费了大量的时间,在安装GD上面。

Ubuntu默认安装Perl, GD是需要libgd的,libgd却在官网上极难下载,下载后却又发现版本不对,这让我在国外互联网上遨游了好几个小时,都要死掉了。。。到最后,我才发现,安装libgd库只要下面这一步就可以了:

apt-get install libgd-gd2-perl

我觉得这就是apt-get方式坑爹的地方,apt get gd 或者libgd根本找不到,如果不去查,谁知道这么写啊! 至于Perl的CPan管理工具,哎,不说了都是泪。

接下来下载gd 2.56,算是非常新的版本。找了各种中文版的安装步骤,发现都有问题。这种事情,最好的办法还是看README.MD啊!

解压之后,perl ./Makefile.PL

之后make

make install

然后就可以了。。。。。。

这份gpx绘制库是这么介绍自己的:

This folder contains several Perl scripts for processing and plotting

GPS track data in .GPX format. 它的readme有不少使用上的说明,当然我们不废话,把所有的gpx数据拷贝到sample_gpx文件夹下,然后华丽丽的运行 ./runme.sh 如果没有问题的话,应该是下面这样:

Python和Perl绘制中国北京跑步地图的方法[/code

我假设各位读者对bash都已经很熟悉了,修改runme.sh文件,可查看更多的选项。 最后得到的结果如下图:

Python和Perl绘制中国北京跑步地图的方法当时看到这个结果,我都惊呆了!这是自己跑了2000公里左右的结果,北京三环内(主要集中在长安街以北)主要的道路都遍了。尤其北三环和北土城路(10号线北段)被我各种虐。每一段白线都是一段故事,每一个点都是我的一个脚印啊!

5.总结

这文章写得显然不够详细,远远没有hand by hand。而且并没有提供更多的数据分析(显然这些工作我都做了)不过相信跑步的程序员一定都很厉害,我这就权作抛砖引玉了。

其实完全可以做成一个web服务,跑友们上传自己的跑步软件的id,就可以自动渲染出各种漂亮的跑步路径和分析图,应该会很有意义吧!

这件事情花费了我七八个小时,简直吐血,大量的时间用在了如何安装GD上,而不是下载数据上。教训告诉我,一定要读安装包里自带的说明文档,因为库和库之间的版本不同,因此可能造成版本地狱,到时候新版本卸载不了,老版本没法用的时候可别说我没提醒啊!

值得一提的是,益动gps下载的gpx文件不带换行符,这导致gpx_disualization库无法解析它(这货正则表达式写错了),我懒得再去动perl正则,于是通过替换增加了换行符。

以上是小编给大家介绍的Python和Perl绘制中国北京跑步地图的方法,希望对大家有所帮助!

Python 相关文章推荐
python简单程序读取串口信息的方法
Mar 13 Python
Python多线程编程(六):可重入锁RLock
Apr 05 Python
浅谈django中的认证与登录
Oct 31 Python
python实现隐马尔科夫模型HMM
Mar 25 Python
python3爬虫之设计签名小程序
Jun 19 Python
PythonWeb项目Django部署在Ubuntu18.04腾讯云主机上
Apr 01 Python
详解Python静态网页爬取获取高清壁纸
Apr 23 Python
解决使用python print打印函数返回值多一个None的问题
Apr 09 Python
如何理解Python中的变量
Jun 01 Python
使用Nibabel库对nii格式图像的读写操作
Jul 01 Python
Python常遇到的错误和异常
Nov 02 Python
python游戏开发之pygame实现接球小游戏
Apr 22 Python
python套接字流重定向实例汇总
Mar 03 #Python
Python设计模式中单例模式的实现及在Tornado中的应用
Mar 02 #Python
Python使用设计模式中的责任链模式与迭代器模式的示例
Mar 02 #Python
详解Python设计模式编程中观察者模式与策略模式的运用
Mar 02 #Python
Python设计模式编程中解释器模式的简单程序示例分享
Mar 02 #Python
分析Python中设计模式之Decorator装饰器模式的要点
Mar 02 #Python
实例解析Python设计模式编程之桥接模式的运用
Mar 02 #Python
You might like
完善CodeIgniter在IDE中代码提示功能的方法
2014/07/19 PHP
php 微信开发获取用户信息如何实现
2016/12/13 PHP
解决css和js的{}与smarty定界符冲突问题的两种方法
2013/09/10 Javascript
jquery ajax post提交数据乱码
2013/11/05 Javascript
Js参数值中含有单引号或双引号问题的解决方法
2013/11/06 Javascript
jquery 隐藏与显示tr标签示例代码
2014/06/06 Javascript
PHP+jQuery实现随意拖动层并即时保存拖动位置
2015/04/30 Javascript
javascript实现控制的多级下拉菜单
2015/07/05 Javascript
jQuery仿360导航页图标拖动排序效果代码分享
2015/08/24 Javascript
jquery+html5时钟特效代码分享(可设置闹钟并且语音提醒)
2020/03/30 Javascript
Javascript oop设计模式 面向对象编程简单实例介绍
2016/12/13 Javascript
从零开始学习Node.js系列教程四:多页面实现数学运算的client端和server端示例
2017/04/13 Javascript
JQuery用$.ajax或$.getJSON跨域获取JSON数据的实现代码
2017/09/23 jQuery
Node.js学习教程之HTTP/2服务器推送【译】
2017/10/31 Javascript
vue-cli的工程模板与构建工具详解
2018/09/27 Javascript
AngularJS $http post 传递参数数据的方法
2018/10/09 Javascript
小程序怎样让wx.navigateBack更好用的方法实现
2019/11/01 Javascript
vuex的使用和简易实现
2021/01/07 Vue.js
[48:31]DOTA2-DPC中国联赛 正赛 Dynasty vs XG BO3 第一场 2月2日
2021/03/11 DOTA
布同自制Python函数帮助查询小工具
2011/03/13 Python
python kmeans聚类简单介绍和实现代码
2018/02/23 Python
python aiohttp的使用详解
2019/06/20 Python
django 消息框架 message使用详解
2019/07/22 Python
python 画图 图例自由定义方式
2020/04/17 Python
浅谈Python 参数与变量
2020/06/20 Python
利用python如何实现猫捉老鼠小游戏
2020/12/04 Python
canvas实现有递增动画的环形进度条的实现方法
2019/07/10 HTML / CSS
Missguided美国官网:英国时尚品牌
2018/01/18 全球购物
SIMON MILLER官网:洛杉矶的生活方式品牌
2020/10/19 全球购物
95%的面试官都会问到的50道Java线程题,附答案
2012/08/03 面试题
工程班组长岗位职责
2013/12/30 职场文书
综治工作心得体会
2014/09/11 职场文书
医生个人自我剖析材料
2014/10/08 职场文书
2015年爱国卫生工作总结
2015/04/22 职场文书
MySql学习笔记之事务隔离级别详解
2021/05/12 MySQL
Python 装饰器(decorator)常用的创建方式及解析
2022/04/24 Python