[导入]Garmin Forerunner TCX file processing

This is a small ruby file I wrote to process the TCX file that I can download from MotionBased website that is processed from the data off my Garmin Forerunner 305. It processes the XML file and generates a little badge/infographic that I can put on a website.


// insert code here..
require "date"
require "rexml/document"
require 'rubygems'
require 'RMagick'

class ForeRunner

M2MI = 1609.344 # meters to miles
MINIMUM_LAP_TIME = 300 # minimum seconds to count as a full lap

attr_accessor :laps, :total_time, :distance, :calories

def initialize(file)
@source_doc = REXML::Document.new file

@laps = 0
@lap_times = Array.new
@lap_bpm = Array.new
@full_laps = 0

@total_time = 0
@distance = 0
@calories = 0
@map_data = []

self.process_file
end

def generate_infographic(output_filename)
canvas = Magick::Image.new(250, 80)
map_size = 50
map_color = 'green'

max_lap_height = 25

gc = Magick::Draw.new

# Draw ellipse
gc.stroke('grey50')
gc.stroke_width(2)
gc.fill_opacity(0)

# draw the relative lap times
lap = 0
max_time = @lap_times.max
@lap_times.each do |s|
lap += 1
x = 10 + (lap * 5)
y = 60 - (s / (max_time / max_lap_height))
gc.line(x, 60, x, y)
end

#draw the heartbeat avg
gc.stroke('#c9a')

lap = 0
lx = nil
ly = nil
max_bpm = @lap_bpm.max
min_bpm = @lap_bpm.min

max_bpm_height = 18

@lap_bpm.each do |s|
lap += 1
x = 10 + (lap * 5)
y = 21 - ( (s - min_bpm) / ((max_bpm - min_bpm) / max_bpm_height))
if !lx
lx = x
ly = y
end

gc.line(lx, ly, x, y)

lx = x
ly = y
end

# draw the map
lat_diff = (@map_data['max_lat'] - @map_data['min_lat']).abs
lon_diff = (@map_data['max_lon'] - @map_data['min_lon']).abs

lat_off = lat_diff / map_size
lon_off = lon_diff / map_size

gc.fill(map_color)
@map_data['map_data'].each do |i|
lt = (map_size - ((i[0] - @map_data['min_lat']) / lat_off)).round
lg = ((i[1] - @map_data['min_lon']) / lon_off).round
gc.point((240 - map_size) + lg, (65 - map_size) + lt)
end

# Annotate
gc.stroke('transparent')
gc.fill('black')
gc.text(120, 15, @distance.to_s[0, 5] + ' mi')
gc.text(120, 30, (@total_time / 60 / 60).to_s[0, 4] + ' hr')
gc.text(120, 45, @calories.to_s + ' cal')

gc.fill('#555')
total_hr = 0
@lap_bpm.each { |hr| total_hr += hr }
gc.text(15, 32, 'avg bpm: ' + (total_hr / @laps).round.to_s)
gc.text(15, 75, 'avg pace: ' + ((@total_time / @full_laps) / 60).to_s[0, 4] + ' min/lap')

gc.draw(canvas)
canvas.write(output_filename)
end

protected

def process_file
@source_doc.elements.each('TrainingCenterDatabase/Activities/Activity/Lap/*') do |element|
if element.name == "TotalTimeSeconds"
@lap_times << element.text.to_f
if element.text.to_f > 300 # for removing warmup and warmdown laps
@total_time += element.text.to_f
@full_laps += 1
end
@laps += 1
end

if element.name == "DistanceMeters"
@distance += (element.text.to_f / M2MI)
end

if element.name == "Calories"
@calories += element.text.to_f
end

if element.name == "AverageHeartRateBpm"
element.elements.each('Value') { |v| @lap_bpm << v.text.to_f }
end
end
@map_data = self.generate_map_points
end

def generate_map_points
map = []

max_lat = -300
max_lon = -300
min_lat = 300
min_lon = 300

@source_doc.elements.each('TrainingCenterDatabase/Activities/Activity/Lap/Track/Trackpoint/*') do |element|
if element.elements['LatitudeDegrees']
lat = element.elements['LatitudeDegrees'].text.to_f
lon = element.elements['LongitudeDegrees'].text.to_f

map << [lat, lon]
max_lat = lat if (lat > max_lat)
max_lon = lon if (lon > max_lon)
min_lat = lat if (lat < min_lat)
min_lon = lon if (lon < min_lon)
end
end
{'map_data' => map, 'max_lat' => max_lat, 'min_lat' => min_lat, 'max_lon' => max_lon, 'min_lon' => min_lon}
end


end


if ARGV[0].nil?
puts "This program takes one argument, a Garmin Forerunner TCX File"
puts "Like this: run.rb filename.tcx"
exit
end

output_filename = ARGV[0].gsub(/\.tcx$/, "-" + Time.now.strftime("%Y%m%d") + ".png")
puts "Generating Graph #{output_filename}"

fr = ForeRunner.new(File.new(ARGV[0]))
fr.generate_infographic(output_filename)

文章来源: http://snippets.dzone.com/posts/show/3712

你可能感兴趣的:(process)