Skip to content

Instantly share code, notes, and snippets.

@mike-kilo
Forked from mrvdb/suuntoapp2gpx
Last active February 2, 2020 15:09
Show Gist options
  • Save mike-kilo/a6cef38223795bc24ee09b2b00c8da56 to your computer and use it in GitHub Desktop.
Save mike-kilo/a6cef38223795bc24ee09b2b00c8da56 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- mode:python -*-
#
# Converts suunto zip files (which contain json files) to a gpx file
# until Suunto gets their act together and let me have my own data in
# a normal way.
# Needs: python 3.7
#
# These zip files are producted by the Suunto app and typically on
# android are located at:
# <Internal Storage>/Android/data/com.stt.suunto/files/smlzip/
#
import sys
import gpxpy
import gpxpy.gpx
import json
import datetime
import zipfile
import math
from backports.datetime_fromisoformat import MonkeyPatch
MonkeyPatch.patch_fromisoformat()
# The relevant json snippet which is in a json array 'Samples'
# looks like this:
# {
# "Attributes": {
# "suunto/sml": {
# "Sample": {
# "GPSAltitude": 94, <-- assuming meters
# "Latitude": 0.88924328690504395, <-- in radians
# "Longitude": 0.10261437316962527, <-- in radians
# "UTC": "2019-04-18T07:25:29.000+00:00" <-- assumed what gps thinks of time?
# }
# }
# },
# "Source": "suunto-123456789", <-- number is the id of the device
# "TimeISO8601": "2019-04-18T09:25:29.000+02:00" <-- assumed what watch thinks of time?
# },
# Suunto specific stuff
SUUNTO_SAMPLES = 'samples.json'
def gpx_track(zip):
# Three keys make a trackpoint
TRKPT = ('Latitude', 'Longitude', 'UTC')
# Create the main gpx object
gpx = gpxpy.gpx.GPX()
# We are getting one track presumably from the points we recorded
track = gpxpy.gpx.GPXTrack()
gpx.tracks.append(track)
# Points are added to segments (take breaks into account later?)
segment = gpxpy.gpx.GPXTrackSegment()
track.segments.append(segment)
# Read in the json file
with zip.open(SUUNTO_SAMPLES, 'r') as json_data:
suunto_json = json.loads(json_data.read().decode('utf-8'))
for s in suunto_json["Samples"]:
if "Sample" in s["Attributes"]["suunto/sml"]:
sample = s["Attributes"]["suunto/sml"]["Sample"]
# See if we have enough for a trackpoint
if all(key in sample for key in TRKPT):
# Lat and long are radians
# To decimal degrees = * 180 / PI
lat = (sample["Latitude"] * 180) / math.pi
lon = (sample["Longitude"] * 180) / math.pi
# I'm assuming this is in meters, and we can use it as is
ele = sample['GPSAltitude'] if 'GPSAltitude' in sample else 0
time = datetime.datetime.fromisoformat(sample['UTC'])
# Create the gpx point
point = gpxpy.gpx.GPXTrackPoint(latitude=lat, longitude=lon,
time=time, elevation=ele)
segment.points.append(point)
# Write out the gpx file
with open(zip.filename.replace(".zip", ".gpx"), "w") as out:
out.write(gpx.to_xml())
if __name__ == "__main__":
# Argument is expected to be a zip file
with zipfile.ZipFile(sys.argv[1]) as zip:
# There should be a samples.json in the zip file
if SUUNTO_SAMPLES in zip.namelist():
gpx_track(zip)
else:
print("No track samples found in zip file!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment