aboutsummaryrefslogtreecommitdiff
path: root/fietsboek/convert.py
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2025-10-19 22:14:35 +0200
committerDaniel Schadt <kingdread@gmx.de>2025-10-28 21:06:37 +0100
commit7b5740fbef267b26c7fc1493736e3edb52fd0173 (patch)
tree86f75d0c947b7e5dbb8fd24c83136d0c0aba820c /fietsboek/convert.py
parent41a5b32d8e613c2d5bf34db1ec89089ad2a98ba7 (diff)
downloadfietsboek-7b5740fbef267b26c7fc1493736e3edb52fd0173.tar.gz
fietsboek-7b5740fbef267b26c7fc1493736e3edb52fd0173.tar.bz2
fietsboek-7b5740fbef267b26c7fc1493736e3edb52fd0173.zip
move GPX reading into convert
Diffstat (limited to 'fietsboek/convert.py')
-rw-r--r--fietsboek/convert.py86
1 files changed, 72 insertions, 14 deletions
diff --git a/fietsboek/convert.py b/fietsboek/convert.py
index d3bfb22..2e8b5db 100644
--- a/fietsboek/convert.py
+++ b/fietsboek/convert.py
@@ -1,7 +1,12 @@
"""Conversion functions to convert between various recording formats."""
+import datetime
+from typing import Optional
import fitparse
-from gpxpy.gpx import GPX, GPXTrack, GPXTrackPoint, GPXTrackSegment
+import gpxpy
+
+from . import geo, util
+from .models import Track
FIT_RECORD_FIELDS = ["position_lat", "position_long", "altitude", "timestamp"]
@@ -15,8 +20,8 @@ def semicircles_to_deg(circles: int) -> float:
return circles * (180 / 2**31)
-def from_fit(data: bytes) -> GPX:
- """Reads a .fit as GPX data.
+def from_fit(data: bytes) -> Track:
+ """Reads a .fit as track data.
This uses the fitparse_ library under the hood.
@@ -26,30 +31,81 @@ def from_fit(data: bytes) -> GPX:
:return: The converted structure.
"""
fitfile = fitparse.FitFile(data)
+ start_time = None
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(
+ time = values["timestamp"]
+ if start_time is None:
+ start_time = time
+ point = geo.Point(
latitude=semicircles_to_deg(values["position_lat"]),
longitude=semicircles_to_deg(values["position_long"]),
elevation=values["altitude"],
- time=values["timestamp"],
+ time_offset=time - start_time,
)
except KeyError:
pass
else:
points.append(point)
- track = GPXTrack()
- track.segments = [GPXTrackSegment(points)]
- gpx = GPX()
- gpx.tracks = [track]
- return gpx
+ path = geo.Path(points)
+ track = Track()
+ track.set_path(path)
+ return track
+
+
+def from_gpx(data: bytes) -> Track:
+ """Reads a .gpx as track data.
+
+ This uses the gpxpy_ library under the hood.
+ .. _gpxpy: https://github.com/tkrajina/gpxpy
-def smart_convert(data: bytes) -> bytes:
+ :param data: The input bytes.
+ :return: The converted structure.
+ """
+ gpx = gpxpy.parse(data)
+ points = []
+ start_time = None
+
+ for track in gpx.tracks:
+ for segment in track.segments:
+ for point in segment.points:
+ if start_time is None:
+ start_time = point.time
+
+ time_offset = (point.time - start_time).total_seconds()
+ points.append(geo.Point(
+ longitude=point.longitude,
+ latitude=point.latitude,
+ elevation=point.elevation,
+ time_offset=time_offset,
+ ))
+
+ timezone = util.guess_gpx_timezone(gpx)
+ date = gpx.time or gpx.get_time_bounds().start_time or datetime.datetime.now()
+ date = date.astimezone(timezone)
+ track_name = gpx.name
+ track_desc = gpx.description
+ for track in gpx.tracks:
+ if not track_name and track.name:
+ track_name = track.name
+ if not track_desc and track.description:
+ track_desc = track.description
+
+ path = geo.Path(points)
+ track = Track()
+ track.set_path(path)
+ track.title = track_name
+ track.description = track_desc
+ track.date = date
+ return track
+
+
+def smart_convert(data: bytes) -> Optional[Track]:
"""Tries to be smart in converting the input bytes.
This function automatically applies the correct conversion if possible.
@@ -61,8 +117,10 @@ def smart_convert(data: bytes) -> 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
+ return from_fit(data)
+ if data.startswith(b"<?xml") and b"<gpx" in data[:200]:
+ return from_gpx(data)
+ return None
-__all__ = ["from_fit", "smart_convert"]
+__all__ = ["from_fit", "from_gpx", "smart_convert"]