将Codoon的路径记录导出成GPX路径

将Codoon的路径记录导出成GPX路径

新浪微博网友 @better_man__ 问道能不能将 Codoon 记录的轨迹文件导出为 GPX 或 TCX 文件,以方便与Garmin、Nike+ Fuelband进行轨迹交换。试了一下,能够完成。过程如下。

GPX 和 TCX 文件

以下内容转自维基百科。

GPX(GPS eXchange Format, GPS交换格式)是一个XML格式,为应用软件设计的通用GPS数据格式。它可以用来描述路点、轨迹、路程。这个格式是免费的,可以在不需要付任何许可费用的前提下使用。它的标签保存位置,海拔和时间,可以用来在不同的GPS设备和软件之间交换数据。TCX, Garmin Training Center XML,功能类此。GARMIN 的 GPS 手表能够使用这两种格式的文件。

这两种格式之间已经有很多互相转换的工具。

实现说明

在 GPX 中,一个没有顺序关系的点集合,叫路点。一个有顺序的点的集合叫轨迹或者路程。轨迹是一个人曾经走过的记录,路程是一个建议的下一步要走的地方。所以,一般来讲,轨迹里的点,包含时间信息,路程里的点,没有时间信息。

下面的代码实现的是将 Codoon 的轨迹数据,转换成 GPX 路径文件。

获取Codoon轨迹数据

利用以前实现的咕咚 Codoon 运动的 API,能够获得某次用户运动的GPS轨迹。对应API详细说明参见 获取咕咚运动移动应用中的数据——非官方API

参考代码:

account = { "email" : "your@email" , "passwd" : "yourpassword" }

imei = "000000000000000"

device = DeviceCodoon ()

# login
device.get_users_login(account["email"], account["passwd"])

# 获得用户的运动历史列表 
routes = device.get_route_log( productId = imei )
for r in routes["data"]:
    routeId = r["route_id"]
    print routeId

# 获得特定某次的运动轨迹
routeId = "03e1cd1e-07b1-11e3-b50f-00163e020001"
route = device.get_single_log( routeId = routeId )
device.saveJsonData( filename = "/single_log_20130817.json" , data = route)

取得的运动轨迹数据可以参考示例文件: single_log_20130817.json。这个轨迹是跟着绿野去长峪城腐败的轨迹。:D,摆两张照片放放毒。

GPX文件输出

Python 中有直接写 GPX 格式文件的包: gpxpy 。 用来创建 GPX 文件非常容易。函数中的参数 route 是 get_single_log 的结果。不过在使用中,如果将时间数据写入 GPX 文件时,gpxpy有个小Bug,需要对时间格式处理的部分做个小调整。

import gpxpy
import gpxpy.gpx

DATE_FORMAT = '%Y-%m-%dT%H:%M:%S'

def trans(route ):
    points = route["data"]["points"]

    # 创建 GPX 文件
    gpx = gpxpy.gpx.GPX()
    # Create first track in our GPX:
    gpx_route = gpxpy.gpx.GPXRoute()

    i = 1
    for p in points:
        tmpname = "#%5d" % i
        tmptime = strptime( p["time_stamp"] , DATE_FORMAT )
        lat = float(p["latitude"])
        lon = float(p["longitude"])

        # Create points
        gpx_point = gpxpy.gpx.GPXRoutePoint( name = tmpname , longitude = lon , latitude = lat ,
            elevation = p["elevation"] , time = tmptime )
        # print gpx_point
        gpx_route.points.append( gpx_point )
        i = i + 1

    gpx.routes.append(gpx_route)

    # print 'Created GPX:', gpx.to_xml()
    return gpx.to_xml()

gpxpy Bug 修正, gpx.py 文件

class GPXRoutePoint(mod_geo.Location):
    ...

    def to_xml(self, version=None):
        content = ''
        if self.elevation is not None:
            content += mod_utils.to_xml('ele', content=self.elevation)
        if self.time:
            # 需要修改的时间格式化部分
            # content += mod_utils.to_xml('time', content=self.time.strftime(DATE_FORMAT))
            content += mod_utils.to_xml('time', content=strftime(DATE_FORMAT , self.time))
        ...


class GPXTrackPoint(mod_geo.Location):
    ...

    def to_xml(self, version=None):
        content = ''

        if self.elevation is not None:
            content += mod_utils.to_xml('ele', content=self.elevation)
        if self.time:
            # 需要修改的时间格式化部分
            # content += mod_utils.to_xml('time', content=self.time.strftime(DATE_FORMAT))
            content += mod_utils.to_xml('time', content=strftime(DATE_FORMAT , self.time))

        ...

所生成的数据文件参见: single_log_20130817_shift.gpx。采用 GPXEditor 显示所生成的文件,发现直接输出的GPX文件在地图上有比较大的漂移,如下图所示,其中红线为真实路径的大致所在:

漂移修正

漂移修正首先需要有与经纬度相对应的offset文件,从 Codoon 的 APK 包中能够找到这个文件: city_offset_txt.txt

在代码中增加读取漂移信息、以及根据起始点计算所需增加的漂移修正的代码即可。代码修正为以下内容:

DATE_FORMAT = '%Y-%m-%dT%H:%M:%S'
CITY_OFFSET = "city_offset_txt.txt"

class CodoonRoute2Gpx:
    offsetList = []

    # 加载漂移数据文件
    def loadCityOffset(self ):
        fn = CITY_OFFSET
        for line in fileinput.FileInput(fn):
            cityInfo = line[:-1].split(";")

            city = cityInfo[0].split(",")
            offset = cityInfo[1].split(",")
            self.offsetList.append( (city , offset) )
            # print self.offsetList

    # 计算与此点最近的偏移数据
    def justifyCityOffset(self , lat ,lon):
        if len( self.offsetList ) == 0 :
            self.loadCityOffset()

        nearest = 180 ** 2 + 180 ** 2

        realoffset = None
        for c in self.offsetList:
            distance = (float(c[0][0]) - lat) ** 2 + (float(c[0][1]) - lon) ** 2
            if distance < nearest :
                nearest = distance
                realoffset = c

        return realoffset[1]

    def trans(self , route ):
        points = route["data"]["points"]

        # 利用GPS起始点,计算在地图上的漂移
        lat = points[0]["latitude"]
        lon = points[0]["longitude"]

        realoffset = self.justifyCityOffset( float(lat) ,float(lon) )
        print realoffset

        # 创建 GPX 文件
        gpx = gpxpy.gpx.GPX()
        # Create first track in our GPX:
        gpx_route = gpxpy.gpx.GPXRoute()


        i = 1
        for p in points:
            tmpname = "#%5d" % i
            tmptime = strptime( p["time_stamp"] , DATE_FORMAT )
            # 漂移修正
            lat = float(p["latitude"]) + float(realoffset[0])
            lon = float(p["longitude"]) + float(realoffset[1])

            # Create points
            gpx_point = gpxpy.gpx.GPXRoutePoint( name = tmpname , longitude = lon , latitude = lat ,
                elevation = p["elevation"] , time = tmptime )
            # print gpx_point
            gpx_route.points.append( gpx_point )
            i = i + 1

        gpx.routes.append(gpx_route)

        # print 'Created GPX:', gpx.to_xml()
        return gpx.to_xml()

所生成的数据文件参见: single_log_20130817.gpx。采用 GPXEditor 显示所生成的文件,漂移已基本修正了:

Track 和 Route

新浪微博网友 @齐亮-Cavendish 发现如上文生成的 GPX 文件,没法导入 heiaheia.com , 经过检查,发现 heiaheia 只能够导入 Track 格式的 GPX 文件。

因此修改 GPX 输出代码如下,增加参数对导出格式的限定。

# Type 可以取值为 "track" or "route", 缺省为 "track"
def trans(self , route , type = "track"):
    name = route["data"]["start_time"]
    points = route["data"]["points"]

    # Calculate offset of start point
    lat = points[0]["latitude"]
    lon = points[0]["longitude"]

    realoffset = self.justifyCityOffset( float(lat) ,float(lon) )
    print realoffset

    gpx = gpxpy.gpx.GPX()

    if type == "route":
        # Create route in GPX Route Format:
        rtname = "Route %s" % name
        gpx_route = gpxpy.gpx.GPXRoute(name = rtname)

        i = 1
        for p in points:
            tmpname = "#%5d" % i
            tmptime = strptime( p["time_stamp"] , DATE_FORMAT )
            lat = float(p["latitude"]) + float(realoffset[0])
            lon = float(p["longitude"]) + float(realoffset[1])

            gpx_point = gpxpy.gpx.GPXRoutePoint( name = tmpname , longitude = lon , latitude = lat ,
                elevation = p["elevation"] , time = tmptime )
            # print gpx_point
            gpx_route.points.append( gpx_point )
            i = i + 1

        gpx.routes.append(gpx_route)
    else:
        # Create route in GPX Track Format:
        trkname = "Track %s" % name
        gpx_track = gpxpy.gpx.GPXTrack(name = trkname)
        gpx_track_seg = gpxpy.gpx.GPXTrackSegment()

        i = 1
        for p in points:
            tmpname = "#%5d" % i
            tmptime = strptime( p["time_stamp"] , DATE_FORMAT )
            lat = float(p["latitude"]) + float(realoffset[0])
            lon = float(p["longitude"]) + float(realoffset[1])

            gpx_point = gpxpy.gpx.GPXTrackPoint( name = tmpname , longitude = lon , latitude = lat ,
                elevation = p["elevation"] , time = tmptime )
            # print gpx_point
            gpx_track_seg.points.append( gpx_point )
            i = i + 1

        gpx_track.segments.append(gpx_track_seg)
        gpx.tracks.append(gpx_track)

    # print 'Created GPX:', gpx.to_xml()
    return gpx.to_xml()

使用 Track 格式的 GPX 文件,能够将数据导入 Heiaheia 网站,能够正确显示轨迹、里程、以及时长。

但是,还有一个问题,文件中Track记录为8月份的数据,并没有被自动放到8月份去,而是放在了当前 Walk 创建时所设定的日期:10月8日。

调用

account = { "email" : "your@email" , "passwd" : "yourpassword" }

imei = "000000000000000"

device = DeviceCodoon ()

# login
device.get_users_login(account["email"], account["passwd"])

# Trans Codoon GPS Data to GPX format
routeId = "03e1cd1e-07b1-11e3-b50f-00163e020001"

trans = CodoonRoute2Gpx()
trans.loadCityOffset()

route = device.get_single_log( routeId = routeId )
device.saveJsonData( filename = "/single_log_20130817.json" , data = route)

gtx = trans.trans( route = route , type = "route" )
device.saveXmlData( filename = "/single_log_20130817_route.gpx" , data = gtx)
gtx = trans.trans( route = route )
device.saveXmlData( filename = "/single_log_20130817_track.gpx" , data = gtx)

代码地址

https://github.com/iascchen/VisHealth/

源代码在 device/codoon.py 中。


玩的开心!


Author : iascchen(at)gmail(dot)com

Date : 2013-10-08

新浪微博 : @问天鼓

原文链接:http://www.wearable.pw/index.php/archives/332



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章