Last active
October 17, 2023 08:48
-
-
Save hutattedonmyarm/70ce9d690c47b43c4ea79c38df298826 to your computer and use it in GitHub Desktop.
Rungap for iOS exports GPS data as JSON file in the free version. This script converts it to GPX. Tested with python3.6. Might need to install required modules. Simply place either the metadata and data json files, or the complete zip file in the same directoryas the script and run it. Warning: Does barely any error checking
This file contains 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
import xml.etree.cElementTree as ElementTree | |
import json, pytz, zipfile, unicodedata, re | |
from datetime import datetime | |
from os import listdir | |
from os.path import isfile, join | |
import glob | |
def slugify(value): | |
""" | |
Normalizes string, converts to lowercase, removes non-alpha characters. | |
Slightly modified from https://github.com/django/django/blob/master/django/utils/text.py | |
""" | |
value = str(value) | |
try: | |
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii') | |
value = re.sub(r'[^\w\s-]', '', value).strip().lower() | |
value = re.sub(r'[-\s]+', '-', value) | |
except: | |
print('Could not slugify filename, will use default one instead') | |
value = 'output' | |
return value | |
creator = "GapX" | |
version = "1.1" | |
zip_files = glob.glob("*.zip") | |
for zip_file in zip_files: | |
print("Unzipping " + zip_file) | |
zip_ref = zipfile.ZipFile(zip_file, 'r') | |
for info in zip_ref.infolist(): | |
print(info.filename) | |
if info.filename.endswith("metadata.json") or info.filename.endswith("rungap.json") or info.filename.endswith("nike.json"): | |
zip_ref.extract(info) | |
zip_ref.close() | |
metadata_files = glob.glob("*metadata.json") | |
data_files = glob.glob("*rungap.json") | |
data_files += glob.glob("*nike.json") | |
print("Found metadata files") | |
print(metadata_files) | |
print("Found data files") | |
print(data_files) | |
if len(metadata_files) != len(data_files): | |
print("Error, number of metadata files does not match number of datafiles!") | |
exit(1) | |
for idx, metadata_file in enumerate(metadata_files): | |
print("=========") | |
print("Parsing " + metadata_file + " and " + data_files[idx]) | |
metadata_data = json.load(open(metadata_file)) | |
data = json.load(open(data_files[idx])) | |
root = ElementTree.Element("gpx") | |
root.set("xmlns","http://www.topografix.com/GPX/1/1") | |
root.set("creator", creator) | |
root.set("version", version) | |
metadata = ElementTree.SubElement(root, "metadata") | |
name = metadata_data["title"] | |
desc = metadata_data["description"] | |
print("Route name: " + name) | |
print("Route description: " + desc) | |
ElementTree.SubElement(metadata, "name").text = name | |
ElementTree.SubElement(metadata, "desc").text = desc | |
ElementTree.SubElement(metadata, "time").text = metadata_data["startTime"]["time"] | |
timezone = pytz.timezone(metadata_data["startTime"].get("timeZone", "UTC")) | |
source = metadata_data["source"] + " exported by Rungap for iOS, version " + metadata_data["appversion"] | |
track = ElementTree.SubElement(root, "trk") | |
ElementTree.SubElement(track, "name").text = name | |
ElementTree.SubElement(track, "desc").text = desc | |
ElementTree.SubElement(track, "src").text = source | |
segment = ElementTree.SubElement(track, "trkseg") | |
if "laps" in data: | |
print("Found " + str(len(data["laps"][0]["points"])) + " track points") | |
for point in data["laps"][0]["points"]: | |
if ("lat" in point and "lon" in point and "ele" in point and "time" in point): | |
trkpt = ElementTree.SubElement(segment, "trkpt", lat=str(point["lat"]), lon=str(point["lon"])) | |
ElementTree.SubElement(trkpt, "ele").text = str(point["ele"]) | |
ElementTree.SubElement(trkpt, "time").text = datetime.fromtimestamp(point["time"], timezone).isoformat() | |
elif "laps" in metadata_data: | |
print("Found " + str(len(metadata_data["laps"])) + " track points") | |
for point in metadata_data["laps"]: | |
p = point.get("startLocation", {}) | |
if ("lat" in p and "lon" in p and "startTime" in point): | |
#dt = datetime.datetime.strptime(d, "%Y-%m-%dT%H:%M:%SZ") | |
trkpt = ElementTree.SubElement(segment, "trkpt", lat=str(p["lat"]), lon=str(p["lon"])) | |
ElementTree.SubElement(trkpt, "time").text = point["startTime"] | |
gpx_filename = slugify(name + " - " + datetime.now().isoformat()) + ".gpx" | |
print("Writing " + gpx_filename) | |
tree = ElementTree.ElementTree(root) | |
tree.write(gpx_filename, "UTF-8", True) |
Author
hutattedonmyarm
commented
Mar 1, 2020
via email
GPX data is (essentially) a list of track points, which contain data such as lat/lon and even heart rate.
So you could do either of these I guess:
• Add all heart rate points as their own trackpoints without setting the GPS data for these.
• Add the last read heart rate to each GPS trackpoint (and throw away the other hr readings)
• Add the average heart rate since the last GPS trackpoint to every trackpoint
…On 1. Mar 2020, 10:43 +0100, Vadim ***@***.***>, wrote:
Hi! It's very nice script. Thank you for that. I've noticed that it doesn't pass heart rate data to .gpx. I've tried to make some changes on my own but got stuck. Problem is that number of gps (lat|lon|ele) points does not match number of heart rate points. And I don't know how to properly map it to each other. Can you add it to your script or give me some ideas how to do that?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
I'm a noob with this programming stuff.
The script first says it cannot find module pytz, if i remove that from the code:
When I run the script the newly created "__MACOSX" folder contains only blank folders with no content.
The script first says it cannot find module pytz, if i remove that from the code
you'll need to install the dependencies. Run pip install pytz
and try again with the original file
Hello
I have installed pytz but the newly created __MACOSX folder contains empty folders
Can you manually unzip the file generated by rungap and tell me what’s in it?
Am 2. Mai 2020, 06:22 +0200 schrieb SpicyStingray :
… @SpicyStingray commented on this gist.
Hello
I have installed pytz but the newly created __MACOSX folder contains empty folders
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
Unzipped it manually then placed every file into one big folder, worked perfectly, thanks
Ah, I think I know what the issue was. Should work with the zip file now too
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment