Last active
September 5, 2024 09:03
-
-
Save pvdb/8287c3dbe74fd41722ee289c289bd3e6 to your computer and use it in GitHub Desktop.
Extract GPX tracks from Garmin FIT files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# frozen_string_literal: true | |
# | |
# fit2gpx - Convert FIT files to GPX files | |
# | |
# INSTALLATION | |
# | |
# ln -s ${PWD}/fit2gpx $(brew --prefix)/bin/ | |
# sudo ln -s ${PWD}/fit2gpx /usr/local/bin/ | |
# | |
# DEPENDENCIES | |
# | |
# gem install fit4ruby | |
# | |
require 'erb' | |
require 'fit4ruby' | |
# | |
# 2024-06-29 18-26-24.fit - FIT file originally recorded on the device | |
# | |
# Track_2024-06-29 18.26.24.gpx - GPX file originally recorded on the device | |
# Track_2024-06-29 18-26-24.gpx - GPX file converted from FIT file on device | |
# Track_2024-06-29 18_26_24.gpx - GPX file converted from FIT file by script | |
# | |
def gpx_path_for(fit_path) | |
fit_file = File.basename(fit_path, '.fit') | |
gpx_file = if fit_file =~ /\d{4}-\d{2}-\d{2} \d{2}-\d{2}-\d{2}/ | |
date, time = fit_file.split | |
converted_time = time.gsub('-', '_') # by script | |
"Track_#{date} #{converted_time}.gpx" | |
else | |
"Track_#{fit_file}.gpx" | |
end | |
File.join(File.dirname(fit_path), gpx_file) | |
end | |
ARGV.each do |fit_path| | |
next unless File.exist?(fit_path) | |
activity = Fit4Ruby.read(fit_path) # parse FIT file | |
time = Time.now # fit2gpx conversion time | |
name = File.basename(fit_path, 'fit') # GPX name == FIT name | |
gpx_path = gpx_path_for(fit_path) | |
gpx_template = ERB.new(DATA.read, trim_mode: '-') | |
File.write(gpx_path, gpx_template.result(binding)) # write GPX file | |
end | |
# That's all Folks! | |
__END__ | |
<?xml version="1.0" encoding="UTF-8" standalone="no" ?> | |
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtrkx="http://www.garmin.com/xmlschemas/TrackStatsExtension/v1" xmlns:wptx1="http://www.garmin.com/xmlschemas/WaypointExtension/v1" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" creator="Oregon 750" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www8.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackStatsExtension/v1 http://www8.garmin.com/xmlschemas/TrackStatsExtension.xsd http://www.garmin.com/xmlschemas/WaypointExtension/v1 http://www8.garmin.com/xmlschemas/WaypointExtensionv1.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd"> | |
<metadata> | |
<link href="http://www.garmin.com"> | |
<text>Garmin International</text> | |
</link> | |
<time><%= time.utc.strftime('%Y-%m-%dT%H:%M:%SZ') -%></time> | |
</metadata> | |
<trk> | |
<name><%= name -%></name> | |
<trkseg> | |
<% activity.records.each do |record| -%> | |
<% lat = sprintf('%.10f', record.position_lat) -%> | |
<% lon = sprintf('%.10f', record.position_long) -%> | |
<% ele = sprintf('%.2f', record.enhanced_elevation) -%> | |
<trkpt lat="<%= lat %>" lon="<%= lon %>"> | |
<ele><%= ele %></ele> | |
<time><%= record.timestamp.utc.strftime('%Y-%m-%dT%H:%M:%SZ') %></time> | |
</trkpt> | |
<% end -%> | |
</trkseg> | |
</trk> | |
</gpx> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment