From a5f74715484bab5837ffb76a110991354983080d Mon Sep 17 00:00:00 2001 From: tkl Date: Tue, 22 Aug 2017 23:02:31 +0200 Subject: [PATCH] gpx-parser: parser implemented Also gpx2html script added. Signed-off-by: Thomas Klaehn --- .gitignore | 1 + .vscode/tags | 8 +++ example-gpx-parser | 51 +++++++++++++++++ gpx2html | 126 +++++++++++++++++++++++++++++++++++++++++ gpx_parser/__init__.py | 82 +++++++++++++++++++++++++++ setup.py | 12 ++++ 6 files changed, 280 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/tags create mode 100755 example-gpx-parser create mode 100755 gpx2html create mode 100644 gpx_parser/__init__.py create mode 100755 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..adbb97d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +data/ \ No newline at end of file diff --git a/.vscode/tags b/.vscode/tags new file mode 100644 index 0000000..ca282b4 --- /dev/null +++ b/.vscode/tags @@ -0,0 +1,8 @@ +!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ +!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ +!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ +!_TAG_PROGRAM_NAME Exuberant Ctags // +!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ +!_TAG_PROGRAM_VERSION 5.9~svn20110310 // +main ../parse.py /^def main():$/;" kind:function line:3 +parse.py ../parse.py 1;" kind:file line:1 diff --git a/example-gpx-parser b/example-gpx-parser new file mode 100755 index 0000000..9872568 --- /dev/null +++ b/example-gpx-parser @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +import argparse +import datetime +import sys +import gpx_parser + + +def parse_args(): + '''Shell argument parser.''' + parser = argparse.ArgumentParser() + parser.add_argument('infolder', help='Specify the in folder.') + return parser.parse_args() + + +def main(): + args = parse_args() + tracks = gpx_parser.Tracks(args.infolder) + for year in range(2017, 2019): + year_distance = 0.0 + year_duration = 0 + for month in range(1, 13): + if month == 12: + max_day = (datetime.date(year + 1, 1, 1) - datetime.timedelta(days=1)).day + else: + max_day = (datetime.date(year, month + 1, 1) - datetime.timedelta(days=1)).day + for day in range(1, max_day): + date_tracks = tracks.get(year, month, day) + date_date = datetime.datetime(year, month, day) + date_dist = 0.0 + date_duration = 0 + for track in date_tracks: + date_dist += track.distance + year_distance += track.distance + date_duration += track.duration.total_seconds() + year_duration += track.duration.total_seconds() + if date_dist > 0.0: + print date_date + print ' Distance: ' + str(round(date_dist / 1000, 2)) + ' km' + print ' Duration: ' + str(datetime.timedelta(seconds=int(round(date_duration, 0)))) + print 'Average speed: ' + str(round(date_dist / date_duration * 3.6, 2)) + ' km/h' + print '==================' + print year + print ' Distance: ' + str(round(year_distance / 1000, 2)) + ' km' + print ' Duration: ' + str(datetime.timedelta(seconds=int(round(year_duration, 0)))) + print 'Average speed: ' + str(round(year_distance / year_duration * 3.6, 2)) + ' km/h' + print '==================' + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/gpx2html b/gpx2html new file mode 100755 index 0000000..eff2fc7 --- /dev/null +++ b/gpx2html @@ -0,0 +1,126 @@ +#!/usr/bin/env python + +import argparse +import datetime +import sys +import gpx_parser +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import numpy +import os + +MONTH_LABELS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] + +def parse_args(): + '''Shell argument parser.''' + parser = argparse.ArgumentParser() + parser.add_argument('infolder', help='Specify the in folder.') + parser.add_argument('outfolder', help='Specify the out folder.') + return parser.parse_args() + + +def plot_bar_chart(labels, ticklabels, values, title, xlabel, ylabel, filename): + fig = plt.figure() + ax1 = fig.add_subplot(111) + ax1.grid(zorder=0) + + plt.title(title) + plt.xlabel(xlabel) + plt.ylabel(ylabel) + + width = 1.0 / len(values) - 0.025 + x_base = numpy.arange(12) + x_pos = list() + + for i in range(len(values)): + x_pos.append([x + (width / 2) + i * width for x in range(len(x_base))]) + plt.bar(x_pos[i], values[i], width=width, label=labels[i], zorder=2) + + plt.xticks(x_base, ticklabels) + plt.legend() + plt.savefig(filename) + + +def main(): + args = parse_args() + tracks = gpx_parser.Tracks(args.infolder) + years_distance = list() + years_avg_spd = list() + years = list() + for year in range(2017, datetime.datetime.now().year + 1): + month_avg_spd = {1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0} + month_distance = {1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0} + month_duration = {1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0, 8:0, 9:0, 10:0, 11:0, 12:0} + for month in range(1, 13): + if month == 12: + max_day = (datetime.date(year + 1, 1, 1) - datetime.timedelta(days=1)).day + else: + max_day = (datetime.date(year, month + 1, 1) - datetime.timedelta(days=1)).day + for day in range(1, max_day): + date_tracks = tracks.get(year, month, day) + for track in date_tracks: + month_distance[month] += (track.distance / 1000) # km + month_duration[month] += track.duration.total_seconds() / 3600 # h + for i in range(1, 13): + if month_duration[i] > 0: + month_avg_spd[i] = month_distance[i] / month_duration[i] + years_distance.append(month_distance.values()) + years_avg_spd.append(month_avg_spd.values()) + years.append(str(year)) + + out_folder = os.path.abspath(args.outfolder) + + dst_file_name = 'distance.png' + plot_bar_chart(years, MONTH_LABELS, years_distance, 'Distance', 'Month', 'km', os.path.join(out_folder, dst_file_name)) + + avg_file_name = 'avg_spd.png' + plot_bar_chart(years, MONTH_LABELS, years_avg_spd, 'Average Speed', 'Month', 'km/h', os.path.join(out_folder, avg_file_name)) + + html_file = os.path.join(out_folder, 'index.html') + with open(html_file, 'w') as handle: + handle.write('\n') + handle.write('\n') + handle.write('\n') + handle.write('\n') + handle.write(' Bicycle \n') + handle.write('\n') + handle.write('\n') + handle.write('
\n') + handle.write('

Bicycle

\n') + handle.write('

\n') + + handle.write('\n') + handle.write('\n') + for year in years: + handle.write('\n'.format(year)) + handle.write('\n') + + handle.write('\n') + for i in range(len(years_distance)): + handle.write('\n'.format(round(sum(years_distance[i]), 1))) + handle.write('\n') + handle.write('
{}
{} km
\n') + + handle.write('

\n') + + handle.write('

\n') + handle.write('Distance\n'.format(dst_file_name)) + handle.write('Distance\n'.format(avg_file_name)) + handle.write('

\n') + + handle.write('\n') + handle.write('
\n') + handle.write('\n') + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/gpx_parser/__init__.py b/gpx_parser/__init__.py new file mode 100644 index 0000000..d028920 --- /dev/null +++ b/gpx_parser/__init__.py @@ -0,0 +1,82 @@ +import datetime +import glob +import os +import gpxpy +import gpxpy.gpx +from geopy import distance +from geopy import Point + +class Segment(object): + start_time = None + end_time = None + distance = 0.0 # [m] + +class Track(object): + start_time = None + end_time = None + distance = 0.0 # [m] + avg_speed = 0.0 # [km/h] + duration = None + + def __init__(self, raw_track): + self.__raw = raw_track + for segment in self.__raw.segments: + seg = Segment() + for i in range(1, len(segment.points)): + if self.start_time is None: + self.start_time = segment.points[i - 1].time + if seg.start_time is None: + seg.start_time = segment.points[i - 1].time + seg.end_time = segment.points[i - 1].time + point1 = Point(str(segment.points[i - 1].latitude) + \ + ' ' + str(segment.points[i - 1].longitude)) + point2 = Point(str(segment.points[i].latitude) + \ + ' ' + str(segment.points[i].longitude)) + seg.distance += distance.distance(point1, point2).meters + + if self.duration is None: + self.duration = seg.end_time - seg.start_time + else: + self.duration += seg.end_time - seg.start_time + self.end_time = seg.end_time + self.distance += seg.distance + self.avg_speed = self.distance / self.duration.total_seconds() * 3.6 + + +class Tracks(object): + track_list = None + + def __init__(self, folder): + self.track_list = list() + gpx_list = glob.glob(os.path.join(folder, '*.gpx')) + for entry in gpx_list: + with open(entry, 'r') as gpx_handle: + gpx = gpxpy.parse(gpx_handle) + for raw_track in gpx.tracks: + self.track_list.append(Track(raw_track)) + + def get(self, year='all', month='all', day='all'): + ret = list() + if year == 'all': + ret = self.track_list + elif month == 'all': + pick_date = datetime.datetime(year=year, month=1, day=1) + for entry in self.track_list: + if pick_date.year == entry.start_time.year: + ret.append(entry) + elif day == 'all': + pick_date = datetime.datetime(year=year, month=month, day=1) + ret = list() + for entry in self.track_list: + if pick_date.year == entry.start_time.year and \ + pick_date.month == entry.start_time.month: + ret.append(entry) + else: + pick_date = datetime.datetime(year=year, month=month, day=day) + ret = list() + for entry in self.track_list: + if pick_date.year == entry.start_time.year and \ + pick_date.month == entry.start_time.month and \ + pick_date.day == entry.start_time.day: + ret.append(entry) + return ret diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..81b92c2 --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +from distutils.core import setup + +NAME = 'gpx_parser' +VERSION = '0.2.0' +AUTHOR = 'Thomas Klaehn' +EMAIL = 'tkl@blackfinn.de' +PACKAGES = [NAME] +SCRIPTS = ['example-gpx-parser', 'gpx2html'] + +setup(name=NAME, version=VERSION, author=AUTHOR, author_email=EMAIL, packages=PACKAGES, scripts=SCRIPTS)