Skip to content

Instantly share code, notes, and snippets.

@ClemRz
Last active March 14, 2019 22:11
Show Gist options
  • Save ClemRz/262fc5c20d93f42ca20587927d3ca18e to your computer and use it in GitHub Desktop.
Save ClemRz/262fc5c20d93f42ca20587927d3ca18e to your computer and use it in GitHub Desktop.
Extracts a CSV speed, elevation and distance table from a KML file.

Extracts a CSV speed, elevation and distance table from a KML file.

This is a Python script that extracts a table with several columns from a KML file: timestamp, elapsed time, distance, speed, elevation, elev. gain, elev. loss and stop time.

How to use this script

Open a terminal and run:

python3 script.py /path/to/your/file.kml

For more help just run:

python script.py -h

Resulting file

The output file is located in the same folder than the input one.

import xml.etree.ElementTree as etree
import datetime
from geopy.distance import geodesic
import re
import argparse
def extract(filename, tz, threshold, verbose):
ns = {"kml": "http://www.opengis.net/kml/2.2"}
outfile = filename.split(".")[0] + ".csv"
tree = etree.parse(filename)
root = tree.getroot()
track1 = root.find("kml:Document/kml:Folder[@id='Tracks']/kml:Folder[@id='track 1']", ns)
points = track1.find("kml:Folder[@id='track 1 points']", ns)
placemarks = points.findall("kml:Placemark", ns)
placemarks_count = len(placemarks)
last_elevation = None
last_coordinates = None
last_timestamp = None
first_timestamp = None
distance = 0.0
stop_time = 0
elev_gain = 0
elev_loss = 0
if verbose:
print(f"Processing {placemarks_count} points")
with open(outfile, 'w') as csv_file:
csv_file.write("TimeStamp,Elapsed Time (s),Distance (km),Speed (km/h),Elevation (m),Elev. gain (m),Elev. loss (m),Stop time (min)\n")
for placemark in placemarks:
timestamp = datetime.datetime.strptime(placemark.find("kml:TimeStamp/kml:when", ns).text, "%Y-%m-%dT%H:%M:%SZ").replace(tzinfo=datetime.timezone(datetime.timedelta(hours=0)))
lng_lat_elev = placemark.find("kml:Point/kml:coordinates", ns).text.split(",")
longitude = float(lng_lat_elev[0])
latitude = float(lng_lat_elev[1])
elevation = float(lng_lat_elev[2])
if first_timestamp is None:
first_timestamp = timestamp
if last_coordinates is not None and last_timestamp is not None:
delta_km = geodesic(last_coordinates, (latitude, longitude)).km
time_spent = (timestamp - last_timestamp).total_seconds()
speed = 3600 * delta_km / time_spent
if speed < threshold:
stop_time = stop_time + time_spent
else:
distance = distance + delta_km
if last_elevation is not None:
delta_elev = elevation - last_elevation
if delta_elev >= 0:
elev_gain += delta_elev
else:
elev_loss += delta_elev
elapsed_time = timestamp - first_timestamp
formatted_stop_time = (datetime.datetime(1,1,1) + datetime.timedelta(seconds=stop_time)).strftime("%H:%M:%S")
formatted_elapsed_time = (datetime.datetime(1,1,1) + elapsed_time).strftime("%H:%M:%S")
formatted_timestamp = timestamp.astimezone(datetime.timezone(datetime.timedelta(hours=tz))).strftime("%Y-%m-%d %H:%M:%S")
line = "{},{},{:.3f},{:.1f},{},{:.0f},{:.0f},{}".format(formatted_timestamp, formatted_elapsed_time, distance, speed, elevation, elev_gain, elev_loss, formatted_stop_time)
if verbose:
print(line)
with open(outfile, 'a+') as csv_file:
csv_file.write(f"{line}\n")
last_coordinates = (latitude, longitude)
last_timestamp = timestamp
last_elevation = elevation
run_time = elapsed_time - datetime.timedelta(seconds=stop_time)
avg_speed = distance / run_time.total_seconds() * 3600
formatted_run_time = (datetime.datetime(1,1,1) + run_time).strftime("%H:%M:%S")
line = "{},{},,{},,{:.2f}km,,+{:.0f}m{:.0f}m,{:.2f}km/h".format(formatted_elapsed_time, formatted_run_time, formatted_stop_time, distance, elev_gain, elev_loss, avg_speed)
if verbose:
print(line)
with open(outfile, 'a+') as csv_file:
csv_file.write("\ntime,run time,,stop time,,distance,,elev. gain-loss,avg. speed\n")
csv_file.write(f"{line}\n")
print(f"File {outfile} created.")
def get_options():
parser = argparse.ArgumentParser(prog='SpeedTable by ClemRz', description='Extracts a CSV speed, elevation and distance table from a KML file.')
parser.add_argument('filename', help='The path to the KML file')
parser.add_argument('-tz', '--timezone', dest='timezone', default='0', help='The destination timezone')
parser.add_argument('-th', '--threshold', dest='threshold', default='3', help='The speed threshold in km/h')
parser.add_argument('-v', '--verbose', dest='verbose', help='Enables verbose output', action='store_true')
return parser.parse_args()
def main():
options = get_options()
extract(options.filename, float(options.timezone), float(options.threshold), options.verbose)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment