"""Top-level package for gpx_converter."""
import gpxpy
import pandas as pd
import numpy as np
import glob
import os
[docs]class Converter(object):
"""main class converter that holds all conversion methods"""
def __init__(self, input_file=None):
if not input_file:
raise Exception("You need to provide an input file!")
else:
input_file_abs_path = os.path.abspath(input_file)
input_file_exists = os.path.exists(input_file_abs_path)
if not input_file_exists:
raise Exception(f"The file {input_file} does not exist.")
self.input_file = input_file_abs_path
self.input_extension = os.path.splitext(input_file)[1].lower()
# print(self.extension)
def _gpx_to_dict(self, lats_colname="latitude", longs_colname="longitude", times_colname="time", alts_colname="altitude"):
longs, lats, times, alts = [], [], [], []
with open(self.input_file, 'r') as gpxfile:
gpx = gpxpy.parse(gpxfile)
for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
lats.append(point.latitude)
longs.append(point.longitude)
times.append(point.time)
alts.append(point.elevation)
if any(times) and any(alts):
return {times_colname: times, lats_colname: lats, longs_colname: longs, alts_colname: alts}
elif any(times):
return {times_colname: times, lats_colname: lats, longs_colname: longs}
elif any(alts):
return {lats_colname: lats, longs_colname: longs, alts_colname: alts}
else:
return {lats_colname: lats, longs_colname: longs}
[docs] def gpx_to_dictionary(self, latitude_key="latitude", longitude_key="longitude", time_key="time", altitude_key="altitude"):
return self._gpx_to_dict(lats_colname=latitude_key, longs_colname=longitude_key, times_colname=time_key, alts_colname=altitude_key)
[docs] def gpx_to_dataframe(self, lats_colname="latitude", longs_colname="longitude", times_colname="time", alts_colname="altitude"):
"""
convert gpx file to a pandas dataframe
lats_colname: name of the latitudes column
longs_colname: name of the longitudes column
times_colname: name of the times column
alts_colname: name of the altitude column
"""
if self.input_extension != ".gpx":
raise TypeError(f"input file must be a GPX file")
if not lats_colname or not longs_colname:
raise TypeError("you must provide the column names of the longitude and latitude")
df = pd.DataFrame.from_dict(self._gpx_to_dict(lats_colname=lats_colname,
longs_colname=longs_colname,
times_colname=times_colname,
alts_colname=alts_colname))
return df
[docs] def gpx_to_numpy_array(self):
df = self.gpx_to_dataframe()
return df.to_numpy()
[docs] def gpx_to_csv(self, lats_colname="latitude", longs_colname="longitude", times_colname="time", alts_colname="altitude", output_file=None):
"""
convert a gpx file to a csv
lats_colname: name of the latitudes column
longs_colname: name of the longitudes column
times_colname: name of the times column
alts_colname: name of the altitude column
output_file: output file where the csv file will be saved
"""
if self.input_extension != ".gpx":
raise TypeError(f"input file must be a GPX file")
if not output_file:
raise Exception("you need to provide an output file!")
output_extension = os.path.splitext(output_file)[1]
if output_extension != ".csv":
raise TypeError(f"output file must be a csv file")
df = self.gpx_to_dataframe(lats_colname=lats_colname,
longs_colname=longs_colname,
times_colname=times_colname,
alts_colname=alts_colname)
df.to_csv(output_file, index=False)
return True
[docs] def gpx_to_excel(self, lats_colname="latitude", longs_colname="longitude", times_colname="time", alts_colname="altitude", output_file=None):
"""
convert a gpx file to a excel
lats_colname: name of the latitudes column
longs_colname: name of the longitudes column
times_colname: name of the times column
alts_colname: name of the altitude column
output_file: output file where the csv file will be saved
"""
if self.input_extension != ".gpx":
raise TypeError(f"input file must be a GPX file")
if not output_file:
raise Exception("you need to provide an output file!")
output_extension = os.path.splitext(output_file)[1]
if output_extension != ".xlsx":
raise TypeError(f"output file must be a excel file (xlsx extension)")
df = self.gpx_to_dataframe(lats_colname=lats_colname,
longs_colname=longs_colname,
times_colname=times_colname,
alts_colname=alts_colname)
if times_colname:
df[times_colname] = df[times_colname].dt.tz_localize(None)
df.to_excel(output_file, index=False)
return True
[docs] def gpx_to_json(self, lats_keyname="latitude", longs_keyname="longitude", times_keyname="time", alts_keyname="altitude", output_file=None):
"""
convert a gpx file to json
lats_keyname: name of the key which will hold all latitude values
longs_keyname: name of the key which will hold all longitude values
times_keyname: name of the key which will hold all time values
alts_keyname: name of the key which will hold all the altitude values
output_file: output file where the csv file will be saved
"""
if self.input_extension != ".gpx":
raise TypeError(f"input file must be a GPX file")
if not output_file:
raise Exception("you need to provide an output file!")
output_extension = os.path.splitext(output_file)[1]
if output_extension != ".json":
raise TypeError(f"output file must be a json file ")
df = self.gpx_to_dataframe(lats_colname=lats_keyname,
longs_colname=longs_keyname,
times_colname=times_keyname,
alts_colname=alts_keyname)
df.to_json(output_file,date_format='iso')
return True
[docs] @staticmethod
def dataframe_to_gpx(input_df, lats_colname='latitude', longs_colname='longitude', times_colname=None, alts_colname=None, output_file=None):
"""
convert pandas dataframe to gpx
input_df: pandas dataframe
lats_colname: name of the latitudes column
longs_colname: name of the longitudes column
times_colname: name of the time column
alts_colname: name of the altitudes column
output_file: path of the output file
"""
if not output_file:
raise Exception("you need to provide an output file!")
output_extension = os.path.splitext(output_file)[1]
if output_extension != ".gpx":
raise TypeError(f"output file must be a gpx file")
import gpxpy.gpx
gpx = gpxpy.gpx.GPX()
# Create first track in our GPX:
gpx_track = gpxpy.gpx.GPXTrack()
gpx.tracks.append(gpx_track)
# Create first segment in our GPX track:
gpx_segment = gpxpy.gpx.GPXTrackSegment()
gpx_track.segments.append(gpx_segment)
# Create points:
for idx in input_df.index:
gpx_segment.points.append(gpxpy.gpx.GPXTrackPoint(input_df.loc[idx, lats_colname],
input_df.loc[idx, longs_colname],
time=pd.Timestamp(input_df.loc[idx, times_colname]) if times_colname else None,
elevation=input_df.loc[idx, alts_colname] if alts_colname else None))
with open(output_file, 'w') as f:
f.write(gpx.to_xml())
return gpx.to_xml()
[docs] def csv_to_gpx(self, lats_colname='latitude', longs_colname='longitude', times_colname=None, alts_colname=None, output_file=None):
"""
convert csv file to gpx
lats_colname: name of the latitudes column
longs_colname: name of the longitudes column
times_colname: name of the time column
alts_colname: name of the altitudes column
output_file: path of the output file
"""
if not output_file:
raise Exception("you need to provide an output file!")
if self.input_extension != ".csv":
raise TypeError(f"input file must be a CSV file")
df = pd.read_csv(self.input_file)
self.dataframe_to_gpx(input_df=df,
lats_colname=lats_colname,
longs_colname=longs_colname,
times_colname=times_colname,
alts_colname=alts_colname,
output_file=output_file)
return True
[docs] def excel_to_gpx(self, lats_colname='latitude', longs_colname='longitude', times_colname=None, alts_colname=None, output_file=None):
"""
convert csv file to gpx
lats_colname: name of the latitudes column
longs_colname: name of the longitudes column
times_colname: name of the time column
alts_colname: name of the altitudes column
output_file: path of the output file
"""
if not output_file:
raise Exception("you need to provide an output file!")
if self.input_extension != ".xlsx":
raise TypeError(f"input file must be a Excel file")
df = pd.read_excel(self.input_file)
self.dataframe_to_gpx(input_df=df,
lats_colname=lats_colname,
longs_colname=longs_colname,
times_colname=times_colname,
alts_colname=alts_colname,
output_file=output_file)
return True
[docs] def json_to_gpx(self, lats_colname='latitude', longs_colname='longitude', times_colname=None, alts_colname=None, output_file=None):
"""
convert csv file to gpx
lats_colname: name of the latitudes column
longs_colname: name of the longitudes column
times_colname: name of the time column
alts_colname: name of the altitudes column
output_file: path of the output file
"""
if not output_file:
raise Exception("you need to provide an output file!")
if self.input_extension != ".json":
raise TypeError(f"input file must be a json file")
df = pd.read_json(self.input_file)
self.dataframe_to_gpx(input_df=df,
lats_colname=lats_colname,
longs_colname=longs_colname,
times_colname=times_colname,
alts_colname=alts_colname,
output_file=output_file)
return True
[docs] @staticmethod
def convert_multi_csv_to_gpx(dirpath, lats_colname='latitude', longs_colname='longitude', times_colname=None, alts_colname=None):
"""
convert multiple csv file from directory to gpx
dirpath: directory path where the csv files are
lats_colname: name of the latitudes columns
longs_colname: name of the longitudes columns
times_colname: name of the time columns
alts_colname: name of the altitudes columns
"""
all_files = glob.glob(dirpath + '/*.csv')
for f in all_files:
gpx_path = f.replace('csv', 'gpx')
df = pd.read_csv(f)
Converter.dataframe_to_gpx(input_df=df,
lats_colname=lats_colname,
longs_colname=longs_colname,
times_colname=times_colname,
alts_colname=alts_colname,
output_file=gpx_path)
[docs] @staticmethod
def spline_interpolation(cv, n=100, degree=3, periodic=False):
""" Calculate n samples on a bspline
cv : Array of control vertices
n : Number of samples to return
degree: Curve degree
periodic: True - Curve is closed
False - Curve is open
"""
# If periodic, extend the point array by count+degree+1
import scipy.interpolate as si
cv = np.asarray(cv)
count = len(cv)
if periodic:
factor, fraction = divmod(count + degree + 1, count)
cv = np.concatenate((cv,) * factor + (cv[:fraction],))
count = len(cv)
degree = np.clip(degree, 1, degree)
# If opened, prevent degree from exceeding count-1
else:
degree = np.clip(degree, 1, count - 1)
# Calculate knot vector
kv = None
if periodic:
kv = np.arange(0 - degree, count + degree + degree - 1, dtype='int')
else:
kv = np.concatenate(([0] * degree, np.arange(count - degree + 1), [count - degree] * degree))
# Calculate query range
u = np.linspace(periodic, (count - degree), n)
# Calculate result
mat = si.splev(u, (kv, cv.T, degree))
return np.array(mat).T
def __repr__(self):
return "class converter that contains all conversion methods."