Created
March 28, 2017 21:11
-
-
Save vicziani/2b099734e8847a26ef0e8ab8d76c33e0 to your computer and use it in GitHub Desktop.
iSki tracker exporter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python | |
| # coding=UTF-8 | |
| """ | |
| Convert html saved from iSki tracker website to gpx format to import | |
| any application or service such QLandkarteGT, Endomondo, etc. | |
| Need to install the following packages: | |
| $ pip install pytz | |
| $ pip install tzlocal | |
| Usage: | |
| $ iski-convert.py <input-file> | |
| Background: | |
| There is no export function on iSki tracker website, but the html source | |
| contains the date, coordinates, profile in different format. This script | |
| converts it to gpx that is a common GPS data format for software | |
| applications. | |
| The script use the locale timezone and convert the dates to UTC, because | |
| the GPS format defines date in Coordinated Universal Time (UTC) using | |
| ISO 8601 format. The Z at the end of the dates is the zone designator for | |
| the zero UTC offset. | |
| """ | |
| import sys | |
| import re | |
| from datetime import datetime, timedelta, date, time | |
| import pytz # $ pip install pytz | |
| from tzlocal import get_localzone # $ pip install tzlocal | |
| import locale | |
| def parsecoordinates(line): | |
| # new google.maps.LatLng(46.68102, 13.89978) | |
| coordinates = [] | |
| pattern = re.compile(r"LatLng\((\d+\.\d+),\ (\d+\.\d+)\)") | |
| for m in re.finditer(pattern, line): | |
| pair = (float(m.group(1)), float(m.group(2))) | |
| coordinates.append(pair) | |
| return coordinates | |
| def parseprofile(line, basedate): | |
| # {"x":46211799.99995232,"y":1435.0,"chartDataIndex":0} | |
| profile = [] | |
| pattern = re.compile(r"\"x\"\:(\d+\.\d+)\,\"y\"\:(\d+\.\d+)") | |
| for m in re.finditer(pattern, line): | |
| pair = (toisoformat(float(m.group(1)), basedate), float(m.group(2))) | |
| profile.append(pair) | |
| return profile | |
| def toisoformat(f, basedate): | |
| local_tz = get_localzone() | |
| d = datetime(basedate.year, basedate.month, basedate.day, tzinfo = local_tz) | |
| d = d + timedelta(milliseconds = f) | |
| d = d.astimezone(pytz.utc) | |
| return d.strftime("%Y-%m-%dT%H:%M:%SZ") | |
| def parsebasedate(line): | |
| d = re.search(r"\d{2}\s\w+\s", line).group(0) | |
| d = str(d) + " " + str(datetime.now().year) | |
| loc = locale.getlocale() | |
| locale.setlocale(locale.LC_TIME, "en_GB.utf8") | |
| basedate = datetime.strptime(d, "%d %b %Y") | |
| locale.setlocale(locale.LC_TIME, loc) | |
| return basedate | |
| if len(sys.argv) != 2: | |
| print "Usage: iski-convert.py <input-file>" | |
| exit() | |
| f = open(sys.argv[1], 'r') | |
| state = None | |
| for line in f: | |
| if state == "DAY": | |
| basedate = parsebasedate(line) | |
| state = None | |
| if """<span class="caption">DAY</span>""" in line: | |
| state = "DAY" | |
| if line.strip().find("new google.maps.LatLng(") >= 0: | |
| coordinates = parsecoordinates(line.strip()) | |
| if line.strip().find("data: ") >= 0: | |
| profile = parseprofile(line.strip(), basedate) | |
| print """<?xml version="1.0" encoding="UTF-8" standalone="no" ?> | |
| <gpx xmlns="http://www.topografix.com/GPX/1/1" creator="byHand" version="1.1" | |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
| xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"> | |
| <trk> | |
| <trkseg>""" | |
| for coordinate,elevation in zip(coordinates, profile): | |
| print """ | |
| <trkpt lon="%f" lat="%f"> | |
| <ele>%f</ele> | |
| <time>%s</time> | |
| </trkpt>""" % (coordinate[1], coordinate[0], elevation[1], elevation[0]) | |
| print """ | |
| </trkseg> | |
| </trk> | |
| </gpx>""" |
I have now written the script but the time format is a little crazy. Can't for the life of me figure out how
51603000 -> 13:30:37.722
54575086 -> 14:20:10.811
…
56794776 -> 14:57:08.491
56795766 -> 14:57:10.488
Any experience with that?
I didn't know, but the ChatGPT helped me. It is milliseconds from midnight. So it does not contain the date! Sample code:
def milliseconds_to_time(milliseconds):
seconds, milliseconds = divmod(milliseconds, 1000)
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
return hours, minutes, seconds, milliseconds
time_in_milliseconds = 35340000
hours, minutes, seconds, milliseconds = milliseconds_to_time(time_in_milliseconds)
print(f"{hours} hours, {minutes} minutes, {seconds} seconds, {milliseconds} milliseconds")The graph on Share page shows always Jan 1. :)
ChatGPTs code seems to return for the examples given above:
51603000 -> 14:20:03 not 13:30:37.722
54575086 -> 15:09:35.86 not 14:20:10.811
…
56794776 -> 15:46:34.776 not 14:57:08.491
56795766 -> 15:46:35.766 not 14:57:10.488
I wrote my own parser for the details file, which is at CatsLover2006/iski2GPX. However, I've noticed that the details file has a very limited number of points to work with.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@vicziani the closest I can find is when you click share on a specific track it references a json file geopath of the format:
{ "path": [ { "lat": 45.090715, "lng": 6.075981, "time": 40792000, "elevation": 1806 }, { "lat": 45.092651, "lng": 6.075863, "time": 41032592, "elevation": 1831 }, { "lat": 45.09284, "lng": 6.075512, "time": 41033592, "elevation": 1829 }, ... }I guess I'll need to write my own parser for that :(