將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



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