-
-
Save vicziani/2b099734e8847a26ef0e8ab8d76c33e0 to your computer and use it in GitHub Desktop.
#!/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 the same error
Traceback (most recent call last):
File "iski-convert.py", line 88, in < module >
profile = parseprofile(line.strip(), basedate)
NameError: name 'basedate' is not defined
Page source code changed.
I did some quick & dirty changes which works for me:
def parsebasedate(line):
d = re.search(r"ski-track of (.+) in", line).group(1)
loc = locale.getlocale()
locale.setlocale(locale.LC_TIME, "en_US")
basedate = datetime.strptime(d, "%b. %d, %Y")
locale.setlocale(locale.LC_TIME, loc)
return basedate
if len(sys.argv) != 2:
print "Usage: iski-convert.py "
exit()
f = open(sys.argv[1], 'r')
state = None
for line in f:
if state == "DAY":
basedate = parsebasedate(line)
state = None
if """<meta property="og:title""" in line:
basedate = parsebasedate(line)
if line.strip().find("new google.maps.LatLng(") >= 0:
coordinates = parsecoordinates(line.strip())
if line.strip().find("data: ") >= 0:
profile = parseprofile(line.strip(), basedate)
Hello,
I exported the Data andnow I have a problem in parsebasedate(line)
I get the following Error:
d = re.search(r"ski-track of (.+) in", line).group(1)
AttributeError: 'nonetype' object has no attribute 'group'
In the data File there is the following line:
<META content="xxx recorded ski-track of Feb. 16, 2018 in xxx"
So the script has no date and can't extract the data.
Is this still working? I can't find any page which contains the gpx data. What page are we supposed to be downloading?
I think it does not work. The site has changed. Choosing the Tracks menu the resource with details
url (see in Chrome Developer Tools) gives back a json file, that contains the data.
For example:
{
"track_id": 1234567,
"startdate": "2018-03-17T09:49:00.000Z",
"duration": 20451.0,
"resort_name": "Fageralm - Forstau",
"distance": 133.272177781605,
"highest_point": 1891.0,
"distance_down": 58.1911639463702,
"distance_up": 75.0810138352348,
"altitude_down": 12639.0,
"altitude_up": 11608.0,
"speed_avg": 14.1840872708436,
"speed_avg_restricted": false,
"speed_max": 58.1643951416016,
"speed_max_restricted": false,
"geometry_processed": true,
"has_dimension_m": true,
"track": "13.72464,47.37496,1760,1521280145.997,0 13.72478,47.37485,1766,1521280238.0,1 13.72586,47.37564,1739,1521280267.0,15 13.72723,47.37648,1680,1521280296.0,17 13.72855,47.3773,1637,1521280326.0,16 13.7297,47.37832,1569,1521280355.0,18 13.73048,47.37792,1557,1521280410.0,5 13.73019,47.37723,1584,1521280446.0,8 13.72963,47.37618,1618,1521280476.0,15 13.72902,47.37518,1673,1521280505.0,15 13.72848,47.37423,1722,1521280534.0,14 13.72795,47.37326,1750,1521280563.0,14 13.72738,47.37229,1782,1521280591.999",
"duration_active": 20451.0,
"duration_passive": 0,
"lifts": 31,
"my_track": true,
"track_type_icon": "icon_skiing.png",
"available_track_types": [
{
"type": "skiing",
"title": "Skiing"
},
{
"type": "snowboarding",
"title": "Snowboarding"
},
{
"type": "cross-country_skiing",
"title": "Cross-country skiing"
},
{
"type": "ski_touring",
"title": "Ski touring"
},
{
"type": "snowshoeing",
"title": "Snowshoeing"
}
]
}
You should parse and convert the track
field.
@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 :(
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.
Hey thanks for writing this script however it is not working for me.
I get the following error:
Traceback (most recent call last):
File "iski-convert.py", line 62, in
profile = parseprofile(line.strip(), basedate)
NameError: name 'basedate' is not defined