Skip to content

Commit

Permalink
#1 Work in progress:
Browse files Browse the repository at this point in the history
-Enhanced Gpx type
-Numerical values precision
-XML properties
-New test files

[ci skip]
  • Loading branch information
FABallemand committed Jun 22, 2023
1 parent 6a814ea commit 1e9ae42
Show file tree
Hide file tree
Showing 15 changed files with 112,434 additions and 3,873 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,5 @@ creator="xsd:string [1] ?">
<trk> trkType </trk> [0..*] ?
<extensions> extensionsType </extensions> [0..1] ?
</gpx>
```
```
- Garmin extensions
4 changes: 2 additions & 2 deletions ezgpx/gpx/gpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(self, file_path: str):
self.file_path: str = file_path
self.parser: Parser = Parser(file_path)
self.gpx: Gpx = self.parser.gpx
self.writer: Writer = Writer()
self.writer: Writer = Writer(self.gpx, precisions=self.parser.precisions)

def nb_points(self) -> int:
"""
Expand Down Expand Up @@ -129,7 +129,7 @@ def to_gpx(self, path: str):
Args:
path (str): Path to the .gpx file.
"""
self.writer.write(self.gpx, path)
self.writer.write(path)

def to_dataframe(self) -> pd.DataFrame:
"""
Expand Down
25 changes: 24 additions & 1 deletion ezgpx/gpx_elements/gpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,30 @@ class Gpx():
Gpx (gpx) element in GPX file.
"""

def __init__(self, metadata: Metadata = None, tracks: list[Track] = []):
def __init__(
self,
creator: str = None,
xmlns: str = None,
version: str = None,
xmlns_xsi: str = None,
xsi_schema_location: list[str] = None,
xmlns_gpxtpx: str = None,
xmlns_gpxx: str = None,
xmlns_gpxtrk: str = None,
xmlns_wptx1: str = None,
metadata: Metadata = None,
tracks: list[Track] = []):
self.creator: str = creator
self.xmlns: str = xmlns
self.version: str = version

self.xmlns_xsi: str = xmlns_xsi
self.xsi_schema_location: str = xsi_schema_location
self.xmlns_gpxtpx: str = xmlns_gpxtpx
self.xmlns_gpxx: str = xmlns_gpxx
self.xmlns_gpxtrk: str = xmlns_gpxtrk
self.xmlns_wptx1: str = xmlns_wptx1

self.metadata: Metadata = metadata
self.tracks: list[Track] = tracks

Expand Down
79 changes: 71 additions & 8 deletions ezgpx/gpx_parser/parser.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,72 @@
from typing import Optional, Union
import logging
from datetime import datetime

import xml.etree.ElementTree as ET

from ..gpx_elements import Bounds, Copyright, Email, Extensions, Gpx, Link, Metadata, Person, TrackPoint, TrackSegment, Track

DEFAULT_PRECISION = 2

class Parser():
"""
GPX file parser.
"""

def __init__(self, file_path: str = ""):
self.file_path = file_path
self.gpx_tree = None
self.gpx_root = None
self.name_space = {"topo": "http://www.topografix.com/GPX/1/1"}
self.gpx = Gpx()
self.file_path: str = file_path
self.gpx_tree: ET.ElementTree = None
self.gpx_root: ET.Element = None
self.name_space: dict = {"topo": "http://www.topografix.com/GPX/1/1"}
self.precisions: dict = {"lat_lon": DEFAULT_PRECISION,
"elevation": DEFAULT_PRECISION,
"distance": DEFAULT_PRECISION,
"duration": DEFAULT_PRECISION,
"speed": DEFAULT_PRECISION,
"rate": DEFAULT_PRECISION}
self.gpx: Gpx = Gpx()

if self.file_path != "":
self.parse()

def check_schema(self):
pass

def find_precision(self, number: str) -> int:

if number is None:
return DEFAULT_PRECISION

try:
test = float(number)

if "." in number:
_, decimal = number.split(sep=".")
return len(decimal)
else:
return 0
except:
logging.error(f"Unable to find precision of number: {number}")
raise

def find_precisions(self):
# Point
track = self.gpx_root.findall("topo:trk", self.name_space)[0]
segment = track.findall("topo:trkseg", self.name_space)[0]
point = segment.findall("topo:trkpt", self.name_space)[0]

self.precisions["lat_lon"] = self.find_precision(point.get("lat"))
self.precisions["elevation"] = self.find_precision(self.find_text(point, "topo:ele"))

# Extensions
extensions = segment.find("topo:extensions", self.name_space)

if extensions is not None:
self.precisions["distance"] = self.find_precision(self.find_text(extensions, "topo:Distance"))
self.precisions["duration"] = self.find_precision(self.find_text(extensions, "topo:TotalElapsedTime"))
self.precisions["speed"] = self.find_precision(self.find_text(extensions, "topo:MovingSpeed"))
self.precisions["rate"] = self.find_precision(self.find_text(extensions, "topo:AvgAscentRate"))

def get_text(self, element, sub_element: str) -> str:
"""
Get text from sub-element.
Expand All @@ -35,10 +79,10 @@ def get_text(self, element, sub_element: str) -> str:
str: Text from sub-element.
"""
try:
text = element.get(sub_element).text
# logging.debug(f"{text} - {type(text)}")
text = element.get(sub_element)
logging.debug(f"{text} - {type(text)}")
except:
logging.DEBUG(f"{element} has no attribute {sub_element}")
logging.debug(f"{element} has no attribute {sub_element}")
text = None
return text

Expand All @@ -60,6 +104,14 @@ def find_text(self, element, sub_element: str) -> str:
text = None
logging.debug(f"{element} has no attribute {sub_element}")
return text

def parse_properties(self):

self.gpx.creator = self.gpx_root.attrib["creator"]
self.gpx.version = self.gpx_root.attrib["version"]
self.gpx.xmlns = self.gpx_root.tag[1:-4]
name_spaces = self.gpx_root.get("{http://www.w3.org/2001/XMLSchema-instance}schemaLocation").split(" ")
self.gpx.xsi_schema_location = [x for x in name_spaces if x != ""]

def parse_bounds(self, bounds) -> Bounds:
"""
Expand Down Expand Up @@ -227,6 +279,7 @@ def parse_point(self, point) -> TrackPoint:
except:
logging.error(f"{point} contains invalid elevation: {self.find_text(point, 'topo:ele')}")
elevation = None

time = datetime.strptime(self.find_text(point, "topo:time"), "%Y-%m-%dT%H:%M:%SZ")

return TrackPoint(lat, lon, elevation, time)
Expand Down Expand Up @@ -315,6 +368,13 @@ def parse(self, file_path: str = "") -> Gpx:
except:
logging.exception("Unable to parse GPX file")
raise

# Parse properties
try:
self.parse_properties()
except:
logging.exception("Unable to parse properties in GPX file")
raise

# Parse metadata
try:
Expand All @@ -330,5 +390,8 @@ def parse(self, file_path: str = "") -> Gpx:
logging.exception("Unable to parse tracks in GPX file")
raise

# Find precision
self.find_precisions()

logging.debug("Parsing complete")
return self.gpx
Loading

0 comments on commit 1e9ae42

Please sign in to comment.