blob: d3bfb22279b7abcbec573f0658c1d31d466ab9b4 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
"""Conversion functions to convert between various recording formats."""
import fitparse
from gpxpy.gpx import GPX, GPXTrack, GPXTrackPoint, GPXTrackSegment
FIT_RECORD_FIELDS = ["position_lat", "position_long", "altitude", "timestamp"]
def semicircles_to_deg(circles: int) -> float:
"""Convert semicircles coordinate to degree coordinate.
:param circles: The coordinate value in semicircles.
:return: The coordinate in degrees.
"""
return circles * (180 / 2**31)
def from_fit(data: bytes) -> GPX:
"""Reads a .fit as GPX data.
This uses the fitparse_ library under the hood.
.. _fitparse: https://pypi.org/project/fitparse/
:param data: The input bytes.
:return: The converted structure.
"""
fitfile = fitparse.FitFile(data)
points = []
for record in fitfile.get_messages("record"):
values = record.get_values()
try:
if any(values[field] is None for field in FIT_RECORD_FIELDS):
continue
point = GPXTrackPoint(
latitude=semicircles_to_deg(values["position_lat"]),
longitude=semicircles_to_deg(values["position_long"]),
elevation=values["altitude"],
time=values["timestamp"],
)
except KeyError:
pass
else:
points.append(point)
track = GPXTrack()
track.segments = [GPXTrackSegment(points)]
gpx = GPX()
gpx.tracks = [track]
return gpx
def smart_convert(data: bytes) -> bytes:
"""Tries to be smart in converting the input bytes.
This function automatically applies the correct conversion if possible.
Note that this function is not guaranteed to return valid GPX bytes. In the worst case,
invalid bytes are simply passed through.
:param data: The input bytes.
:return: The converted content.
"""
if len(data) > 11 and data[9:12] == b"FIT":
return from_fit(data).to_xml().encode("utf-8")
return data
__all__ = ["from_fit", "smart_convert"]
|