Skip to content

Commit

Permalink
#3 Work in progress:
Browse files Browse the repository at this point in the history
-Create parent class Writer for GPXWriter and KMLWriter
-Add tests for GPX class

[ci skip]
  • Loading branch information
FABallemand committed Sep 9, 2023
1 parent 862bc85 commit 4d70364
Show file tree
Hide file tree
Showing 13 changed files with 2,143 additions and 172 deletions.
1 change: 1 addition & 0 deletions ezgpx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .gpx_elements import *
from .gpx_parser import *
from .writer import *
from .gpx_writer import *
from .kml_writer import *
from .gpx import *
4 changes: 2 additions & 2 deletions ezgpx/gpx/gpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self, file_path: Optional[str] = None, check_schemas: bool = True,
pass

def __str__(self) -> str:
return f"file_path = {self.file_path}\nparser = {self.parser}\ngpx = {self.gpx}\nwriter = {self.gpx_writer}"
return f"file_path = {self.file_path}\nparser = {self.parser}\ngpx = {self.gpx}\ngpx_writer = {self.gpx_writer}\nkml_writer = {self.kml_writer}"

def check_schemas(self, extensions_schemas: bool = False) -> bool:
"""
Expand Down Expand Up @@ -566,7 +566,7 @@ def to_csv(
str
CSV like string if path is set to None.
"""
return self.gpx.to_csv(columns, path, sep, header, index)
return self.gpx.to_csv(path, sep, columns, header, index)

def to_gpx(self, path: str, check_schemas: bool = True, extensions_schemas: bool = False) -> bool:
"""
Expand Down
2 changes: 1 addition & 1 deletion ezgpx/gpx_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(self, file_path: Optional[str] = None, check_schemas: bool = True,
"""
self.file_path: str = file_path
self.check_schemas: bool = check_schemas
self.extensions_schemass: bool = extensions_schemas
self.extensions_schemas: bool = extensions_schemas

self.gpx_tree: ET.ElementTree = None
self.gpx_root: ET.Element = None
Expand Down
78 changes: 7 additions & 71 deletions ezgpx/gpx_writer/gpx_writer.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import os
from typing import Optional, Union, Tuple
from typing import Optional, Union, List, Tuple, Dict
import logging
import xml.etree.ElementTree as ET
from datetime import datetime

from ..writer import Writer
from ..gpx_elements import Bounds, Copyright, Email, Extensions, Gpx, Link, Metadata, Person, PointSegment, Point, Route, TrackSegment, Track, WayPoint
from ..gpx_parser import Parser, DEFAULT_PRECISION, DEFAULT_TIME_FORMAT
from ..gpx_parser import Parser

class GPXWriter():
class GPXWriter(Writer):
"""
GPX file writer.
"""
Expand All @@ -23,7 +24,7 @@ def __init__(
extensions: bool = True,
ele: bool = True,
time: bool = True,
precisions: dict = None,
precisions: Dict = None,
time_format: str = None) -> None:
"""
Initialize GPXWriter instance.
Expand All @@ -41,8 +42,7 @@ def __init__(
precisions (dict, optional): Decimal precision for each type of value. Defaults to None.
time_format (dict, optional): Time format. Defaults to None.
"""
self.gpx: Gpx = gpx
self.path: str = path
super().__init__(gpx, path)
self.gpx_string: str = ""

# Parameters
Expand All @@ -54,74 +54,10 @@ def __init__(
self.ele: bool = ele
self.time: bool = time

self.precisions: dict = precisions
self.precisions: Dict = precisions
self.time_format = time_format

self.gpx_root = None

def add_subelement(self, element: ET.Element, sub_element: str, text: str) -> Tuple[ET.Element, Union[ET.Element, None]]:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
text (str): GPX sub-element text.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if text is not None:
sub_element_ = ET.SubElement(element, sub_element)
sub_element_.text = text
return element, sub_element_

def add_subelement_number(self, element: ET.Element, sub_element: str, number: Union[int, float], precision: int = DEFAULT_PRECISION) -> Tuple[ET.Element, Union[ET.Element, None]]:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
number (Union[int, float], optional): GPX sub-element value.
precision (int, optional): Precision. Defaults to DEFAULT_PRECISION.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if number is not None:
sub_element_ = ET.SubElement(element, sub_element)
if type(number) is int:
sub_element_.text = str(number)
elif type(number) is float:
sub_element_.text = "{:.{}f}".format(number, precision)
else:
logging.error("Invalid number type.")
return element, sub_element_

def add_subelement_time(self, element: ET.Element, sub_element: str, time: datetime, format: str = DEFAULT_TIME_FORMAT) -> ET.Element:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
time (datetime, optional): GPX sub-element value.
format (str, optional): Format. Defaults to DEFAULT_TIME_FORMAT.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if time is not None:
sub_element_ = ET.SubElement(element, sub_element)
try:
sub_element_.text = time.strftime(format)
except:
logging.error("Invalid time format.")
return element, sub_element_

def add_bounds(self, element: ET.Element, bounds: Bounds) -> ET.Element:
"""
Expand Down
75 changes: 5 additions & 70 deletions ezgpx/kml_writer/kml_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import xmlschema
from datetime import datetime

from ..gpx_elements import Bounds, Copyright, Email, Extensions, Gpx, Link, Metadata, Person, PointSegment, Point, Route, TrackSegment, Track, WayPoint
from ..gpx_parser import Parser, DEFAULT_PRECISION, DEFAULT_TIME_FORMAT
from ..writer import Writer
from ..gpx_elements import Gpx

DEFAULT_NORMAL_STYLE = {"color": "ff0000ff",
"width": 2,
Expand All @@ -22,7 +22,7 @@
("highlight", DEFAULT_HIGHLIGHT_STYLE)
]

class KMLWriter():
class KMLWriter(Writer):
"""
KML file writer.
"""
Expand All @@ -38,14 +38,13 @@ def __init__(
extensions: bool = True,
ele: bool = True,
time: bool = True,
precisions: dict = None,
precisions: Dict = None,
time_format: str = None,
styles: List[Tuple[str, Dict]] = DEFAULT_STYLES) -> None:
"""
Initialize GPXWriter instance.
"""
self.gpx: Gpx = gpx
self.path: str = path
super().__init__(gpx, path)
self.file_name: str = ""
self.kml_string: str = ""

Expand All @@ -64,70 +63,6 @@ def __init__(
self.styles = styles

self.kml_root = None

def add_subelement(self, element: ET.Element, sub_element: str, text: str) -> Tuple[ET.Element, Union[ET.Element, None]]:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
text (str): GPX sub-element text.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if text is not None:
sub_element_ = ET.SubElement(element, sub_element)
sub_element_.text = text
return element, sub_element_

def add_subelement_number(self, element: ET.Element, sub_element: str, number: Union[int, float], precision: int = DEFAULT_PRECISION) -> Tuple[ET.Element, Union[ET.Element, None]]:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
number (Union[int, float], optional): GPX sub-element value.
precision (int, optional): Precision. Defaults to DEFAULT_PRECISION.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if number is not None:
sub_element_ = ET.SubElement(element, sub_element)
if type(number) is int:
sub_element_.text = str(number)
elif type(number) is float:
sub_element_.text = "{:.{}f}".format(number, precision)
else:
logging.error("Invalid number type.")
return element, sub_element_

def add_subelement_time(self, element: ET.Element, sub_element: str, time: datetime, format: str = DEFAULT_TIME_FORMAT) -> ET.Element:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
time (datetime, optional): GPX sub-element value.
format (str, optional): Format. Defaults to DEFAULT_TIME_FORMAT.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if time is not None:
sub_element_ = ET.SubElement(element, sub_element)
try:
sub_element_.text = time.strftime(format)
except:
logging.error("Invalid time format.")
return element, sub_element_

def add_pair(self, element: ET.Element, key: str, style_url: str) -> ET.Element:
"""
Expand Down
1 change: 1 addition & 0 deletions ezgpx/writer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .writer import *
94 changes: 94 additions & 0 deletions ezgpx/writer/writer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import os
from typing import Optional, Union, Tuple
import logging
import xml.etree.ElementTree as ET
from datetime import datetime

from ..gpx_elements import Gpx
from ..gpx_parser import DEFAULT_PRECISION, DEFAULT_TIME_FORMAT

class Writer():
"""
File writer.
"""

def __init__(
self,
gpx: Gpx = None,
path: str = None) -> None:
"""
Initialize Writer instance.
Parameters
----------
gpx : Gpx, optional
Gpx instance to write, by default None
path : str, optional
Path to the file to write, by default None
"""
self.gpx: Gpx = gpx
self.path: str = path

def add_subelement(self, element: ET.Element, sub_element: str, text: str) -> Tuple[ET.Element, Union[ET.Element, None]]:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
text (str): GPX sub-element text.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if text is not None:
sub_element_ = ET.SubElement(element, sub_element)
sub_element_.text = text
return element, sub_element_

def add_subelement_number(self, element: ET.Element, sub_element: str, number: Union[int, float], precision: int = DEFAULT_PRECISION) -> Tuple[ET.Element, Union[ET.Element, None]]:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
number (Union[int, float], optional): GPX sub-element value.
precision (int, optional): Precision. Defaults to DEFAULT_PRECISION.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if number is not None:
sub_element_ = ET.SubElement(element, sub_element)
if type(number) is int:
sub_element_.text = str(number)
elif type(number) is float:
sub_element_.text = "{:.{}f}".format(number, precision)
else:
logging.error("Invalid number type.")
return element, sub_element_

def add_subelement_time(self, element: ET.Element, sub_element: str, time: datetime, format: str = DEFAULT_TIME_FORMAT) -> ET.Element:
"""
Add sub-element to GPX element.
Args:
element (xml.etree.ElementTree.Element): GPX element.
sub_element (str): GPX sub-element.
time (datetime, optional): GPX sub-element value.
format (str, optional): Format. Defaults to DEFAULT_TIME_FORMAT.
Returns:
Tuple[xml.etree.ElementTree.Element, Union[xml.etree.ElementTree.Element, None]]: GPX element and GPX sub-element (if not None).
"""
sub_element_ = None
if time is not None:
sub_element_ = ET.SubElement(element, sub_element)
try:
sub_element_.text = time.strftime(format)
except:
logging.error("Invalid time format.")
return element, sub_element_
4 changes: 2 additions & 2 deletions notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

## 📝 TO DO LIST !!
- Change to project.toml (https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html)
- Sub classes for GPX & KML writers
- Merge method
- Garmin extensions
- Compute length properly (improve haversine distance precision, 2D, 3D)
- Compute length properly (improve haversine distance precision, 2D, 3D)
- Tests
Loading

0 comments on commit 4d70364

Please sign in to comment.