// 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)
You need to create an account or log in to post comments to this site.