"""
This module contains the FitParser class.
"""
from pathlib import Path
from typing import IO
from fitparse import FitFile
from ..complex_types import Trk, Trkseg, Wpt
from ..constants.precisions import DEFAULT_PRECISION
from .parser import Parser
[docs]
class FitParser(Parser):
"""
Fit file parser.
"""
_semicircles_to_deg_const = 180 / 2**31
def __init__(self, source: str | Path | IO[str] | IO[bytes] | bytes) -> None:
"""
Initialise FitParser instance.
Args:
source (str | Path | IO[str] | IO[bytes] | bytes): Path to a
file or a file-like object to parse.
"""
super().__init__(source)
def _semicircles_to_deg(self, semicircle_coord: list) -> list:
"""
Convert semicircle data from FIT file to dms data.
Args:
semicircle_coord (list): list of semicircle values.
Returns:
list: list of dms values.
"""
return [FitParser._semicircles_to_deg_const * x for x in semicircle_coord]
def _parse(self):
"""
Parse FIT file and store data in a Gpx element.
"""
lat_data = []
lon_data = []
alt_data = []
time_data = []
units = {"alt": "", "lat": "", "lon": ""}
fit_file = FitFile(self.source)
for record in fit_file.get_messages("record"):
for record_data in record:
if record_data.name == "position_lat":
lat_data.append(record_data.value)
if units["lat"] == "":
units["lat"] = record_data.units
# Temporary (may change if units["lat"] == "semicircles")
self.precisions["lat_lon"] = self._find_precision(
record_data.value
)
if record_data.name == "position_long":
lon_data.append(record_data.value)
if units["lon"] == "":
units["lon"] = record_data.units
if record_data.name == "altitude":
alt_data.append(record_data.value)
if units["alt"] == "":
units["alt"] = record_data.units
self.precisions["elevation"] = self._find_precision(
record_data.value
)
if record_data.name == "timestamp":
time_data.append(record_data.value)
self._find_time_format(record_data.value)
# Convert semicircles data to radians ??
if units["lat"] == "semicircles":
self.precisions["lat_lon"] = DEFAULT_PRECISION
lat_data = self._semicircles_to_deg(lat_data)
if units["lon"] == "semicircles":
lon_data = self._semicircles_to_deg(lon_data)
# Store FIT data in Gpx element
trkpt = [
Wpt("trkpt", lat, lon, alt, time)
for lat, lon, alt, time in zip(lat_data, lon_data, alt_data, time_data)
]
trkseg = Trkseg(trkpt=trkpt)
trk = Trk(trkseg=[trkseg])
self.gpx.trk = [trk]
def _add_properties(self):
self.gpx.creator = "ezGPX"
self.gpx.xmlns = "http://www.topografix.com/GPX/1/1"
self.gpx.version = "1.1"
self.gpx.xmlns_xsi = "http://www.w3.org/2001/XMLSchema-instance"
self.gpx.xsi_schema_location = [
"http://www.topografix.com/GPX/1/1",
"http://www.topografix.com/GPX/1/1/gpx.xsd",
]
[docs]
def parse(self) -> dict:
"""
Parse Fit file.
dict: Gpx, precisions and time_format.
Example:
>>> # TODO
"""
# Parse FIT file
self._parse()
# Add properties
self._add_properties()
return {
"gpx": self.gpx,
"precisions": self.precisions,
"time_format": self.time_format,
}