Skip to content

Commit

Permalink
#2 Work in progress:
Browse files Browse the repository at this point in the history
-Track extensions class
-New GPX methods
  • Loading branch information
FABallemand committed Jun 14, 2023
1 parent 550d5dd commit bd6e865
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 67 deletions.
116 changes: 50 additions & 66 deletions ezgpx/gpx/gpx.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from typing import *
import pandas as pd
import matplotlib.pyplot as plt

import logging

from ..gpx_elements import Gpx
from ..gpx_parser import Parser
from ..gpx_writer import Writer
Expand All @@ -28,6 +29,15 @@ def nb_points(self):
for track_segment in track.track_segments:
nb_pts += len(track_segment.track_points)
return nb_pts

def distance(self):
"""
Returns the distance (meters) of the tracks contained in the GPX.
Returns:
float: Distance (meters).
"""
return self.gpx.distance()

def to_string(self) -> str:
return self.writer.gpx_to_string(self.gpx)
Expand All @@ -44,71 +54,45 @@ def to_dataframe(self) -> pd.DataFrame:
"""
return self.gpx.to_dataframe()

# def removeGPSErrors(gpx, error_distance=1000):
# """
# Remove GPS errors.

# Args:
# gpx (GPX): GPX object.
# error_distance (int, optional): GPS error threshold distance (in meters) between two points. Defaults to 1000.

# Returns:
# GPX: GPX object without GPS error.
# list: List of removed points (GPS errors).
# """
# # Create new "file"
# cleaned_gpx = gpxpy.gpx.GPX()

# previous_point = None
# GPS_errors = []

# for track in gpx.tracks:
# # Create track
# gpx_track = gpxpy.gpx.GPXTrack()
# cleaned_gpx.tracks.append(gpx_track)
# for segment in track.segments:
# # Create segment
# gpx_segment = gpxpy.gpx.GPXTrackSegment()
# gpx_track.segments.append(gpx_segment)
# for point in segment.points:
# # Create points
# if previous_point is None or gpxpy.geo.haversine_distance(previous_point.latitude,
# previous_point.longitude,
# point.latitude,
# point.longitude) < error_distance:
# gpx_segment.points.append(gpxpy.gpx.GPXTrackPoint(point.latitude, point.longitude, elevation=point.elevation))
# previous_point = point
# else:
# GPS_errors.append(point)
# return cleaned_gpx, GPS_errors

# def compressFile(gpx, compression_method="Ramer-Douglas-Peucker algorithm", vertical_smooth=True, horizontal_smooth=True):
# """
# Compress GPX file.

# Args:
# gpx (GPX): GPX object.
# compression_method (str, optional): Method used to compress GPX. Defaults to "RPD".
# vertical_smooth (bool, optional): Vertical smoothing. Defaults to True.
# horizontal_smooth (bool, optional): Horizontal smoothing. Defaults to True.

# Returns:
# GPX: Compressed GPX object.
# """
# # Smoothing
# gpx.smooth(vertical=vertical_smooth, horizontal=horizontal_smooth)

# # Compression
# if compression_method == "Ramer-Douglas-Peucker algorithm":
# gpx.simplify()
# elif compression_method == "Remove 25% points":
# gpx.reduce_points(int(gpx.get_track_points_no() * 0.75))
# elif compression_method == "Remove 50% points":
# gpx.reduce_points(int(gpx.get_track_points_no() * 0.5))
# elif compression_method == "Remove 75% points":
# gpx.reduce_points(int(gpx.get_track_points_no() * 0.25))

# return gpx
def remove_metadata(self):
"""
Remove metadata (ie: metadata will not be written when saving the GPX object as a .gpx file).
"""
self.writer.metadata = False

def remove_elevation(self):
"""
Remove elevation data (ie: elevation data will not be written when saving the GPX object as a .gpx file).
"""
self.writer.ele = False

def remove_time(self):
"""
Remove time data (ie: time data will not be written when saving the GPX object as a .gpx file).
"""
self.writer.time = False

def remove_gps_errors(self):
self.gpx.remove_gps_errors()

def compress(self, compression_method: str = "Ramer-Douglas-Peucker algorithm"):
"""
Compress GPX by removing points.
Args:
compression_method (str, optional): Method used to compress GPX. Defaults to "Ramer-Douglas-Peucker algorithm".
"""
if compression_method == "Ramer-Douglas-Peucker algorithm":
logging.debug("Ramer-Douglas-Peucker algorithm is not implemented yet")
pass
elif compression_method == "Remove 25% points":
self.gpx.remove_points(4)
elif compression_method == "Remove 50% points":
self.gpx.remove_points(2)
elif compression_method == "Remove 75% points":
pass
elif compression_method == "Remove elevation":
logging.debug("Removing elevation is not implemented yet")

def plot(self, title: str = "Track", base_color: str = "#101010", start_stop: bool = False, elevation_color: bool = False, file_path: str = None,):

Expand Down
1 change: 1 addition & 0 deletions ezgpx/gpx_elements/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .gpx import *
from .metadata import *
from .track import *
from .track_extensions import *
from .track_segment import *
from .track_point import *
47 changes: 47 additions & 0 deletions ezgpx/gpx_elements/gpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from .metadata import *
from .track import *

from ..utils import haversine_distance

class Gpx():
"""
Gpx (gpx) element in GPX file.
Expand All @@ -12,6 +14,24 @@ def __init__(self, metadata: Metadata = None, tracks: list[Track] = []):
self.metadata: Metadata = metadata
self.tracks: list[Track] = tracks

def distance(self):
"""
Compute the total distance (in meters) of the tracks contained in the Gpx element.
Returns:
float: Distance (meters)
"""
dst = 0
previous_latitude = self.tracks[0].track_segments[0].track_points[0].latitude
previous_longitude = self.tracks[0].track_segments[0].track_points[0].longitude
for track in self.tracks:
for track_segment in track.track_segments:
for track_point in track_segment.track_points:
dst += haversine_distance(previous_latitude, previous_longitude, track_point.latitude, track_point.longitude)
previous_latitude = track_point.latitude
previous_longitude = track_point.longitude
return dst

def to_dataframe(self) -> pd.DataFrame:
"""
Convert Gpx element to Pandas Dataframe.
Expand All @@ -30,6 +50,33 @@ def to_dataframe(self) -> pd.DataFrame:
})
df = pd.DataFrame(route_info)
return df

def remove_gps_errors(self, error_distance=1000):
"""
Remove GPS errors.
Args:
error_distance (int, optional): GPS error threshold distance (meters) between two points. Defaults to 1000.
Returns:
list: List of removed points (GPS errors).
"""
previous_point = None
gps_errors = []

for track in self.tracks:
for track_segment in track.track_segments:
for track_point in track_segment.track_points:
# Create points
if previous_point is not None and haversine_distance(previous_point.latitude,
previous_point.longitude,
track_point.latitude,
track_point.longitude) < error_distance:
gps_errors.append(track_point)
track_segment.track_points.remove(track_point)
else:
previous_point = track_point
return gps_errors

def remove_points(self, remove_factor: int = 2):
count = 0
Expand Down
4 changes: 3 additions & 1 deletion ezgpx/gpx_elements/track.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from track_extensions import *
from .track_segment import *

class Track():
"""
Track (trk) element in GPX file.
"""

def __init__(self, name: str = "", track_segments: list[TrackSegment] = []):
def __init__(self, name: str = "", track_extensions: TrackExtensions = None, track_segments: list[TrackSegment] = []):
self.name: str = name
self.track_extensions: TrackExtensions = track_extensions
self.track_segments: list[TrackSegment] = track_segments
38 changes: 38 additions & 0 deletions ezgpx/gpx_elements/track_extensions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

class TrackExtensions():
"""
Track extensions.
"""

def __init__(
self,
display_color: str = "Cyan",
distance: float = None,
total_elapsed_time: float = None,
moving_time: float = None,
stopped_time: float = None,
moving_speed: float = None,
max_speed: float = None,
max_elevation: float = None,
min_elevation: float = None,
ascent: float = None,
descent: float = None,
avg_ascent_rate: float = None,
max_ascent_rate: float = None,
avg_descent_rate: float = None,
max_descent_rate: float = None) -> None:
self.display_color: str = display_color
self.distance: float = distance
self.total_elapsed_time: float = total_elapsed_time # int?
self.moving_time: float = moving_time # int?
self.stopped_time: float = stopped_time
self.moving_speed: float = moving_speed
self.max_speed: float = max_speed
self.max_elevation: float = max_elevation
self.min_elevation: float = min_elevation
self.ascent: float = ascent
self.descent: float = descent
self.avg_ascent_rate: float = avg_ascent_rate
self.max_ascent_rate: float = max_ascent_rate
self.avg_descent_rate: float = avg_descent_rate
self.max_descent_rate: float = max_descent_rate
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit bd6e865

Please sign in to comment.