gooby 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +96 -43
- data/bin/example_usage.rb +15 -6
- data/bin/tcx_ex.rb +35 -0
- data/data/2007_03_04.tcx +16751 -0
- data/data/activity_2007_03_04_15_22_36.xml +10545 -0
- data/lib/gooby/cls_gooby_command.rb +14 -3
- data/lib/gooby/cls_training_center_parser.rb +183 -0
- data/lib/gooby/cls_training_center_splitter.rb +109 -0
- data/lib/gooby/mod_project_info.rb +1 -1
- data/lib/gooby.rb +303 -5
- data/pkg/pkg.rb +2 -0
- data/tests/tc_mod_project_info.rb +1 -1
- data/tests/ts_gooby.rb +2 -2
- metadata +7 -21
- data/data/20051119_dowd_ymca_hm.csv +0 -251
- data/data/20051119_dowd_ymca_hm.xml +0 -2210
- data/data/run_2007_01_06_15_27_31.xml +0 -2020
- data/data/run_2007_01_10_12_25_47.xml +0 -820
- data/data/run_2007_01_10_22_44_54.csv +0 -112
- data/data/run_2007_01_10_22_44_54.xml +0 -908
- data/data/run_2007_01_11_10_48_45.xml +0 -1292
- data/data/run_2007_01_13_15_37_06.xml +0 -1964
- data/data/run_2007_01_14_15_46_02.xml +0 -1368
- data/data/run_2007_01_15_14_01_48.xml +0 -1868
- data/data/run_2007_01_20_16_22_05.xml +0 -1702
- data/data/run_2007_01_27_17_32_13.xml +0 -3626
- data/data/run_2007_01_28_19_14_52.xml +0 -2538
- data/data/run_2007_02_03_14_30_20.xml +0 -2016
- data/data/run_2007_02_04_18_02_30.xml +0 -1476
- data/data/run_2007_02_17_16_29_35.xml +0 -1236
- data/data/run_2007_02_19_14_44_33.xml +0 -1816
- data/data/run_2007_02_23_15_53_55.xml +0 -36
- data/data/run_2007_02_23_15_55_20.xml +0 -1296
@@ -14,17 +14,28 @@ module Gooby
|
|
14
14
|
Gooby::Options.new(yaml_filename)
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def split_garmin_forerunner_logbook_xml(xml_filename, out_dir)
|
18
18
|
splitter = Gooby::ForerunnerXmlSplitter.new(xml_filename, out_dir)
|
19
19
|
splitter.split
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
22
|
+
def split_garmin_training_center_xml(tcx_filename, out_dir)
|
23
|
+
splitter = Gooby::TrainingCenterXmlSplitter.new(tcx_filename, out_dir)
|
24
|
+
splitter.split
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_garmin_forerunner_logbook_xml(xml_filename)
|
23
28
|
handler = Gooby::ForerunnerXmlParser.new
|
24
29
|
Document.parse_stream((File.new xml_filename), handler)
|
25
30
|
handler.put_all_run_tkpt_csv(true)
|
26
31
|
end
|
27
|
-
|
32
|
+
|
33
|
+
def parse_garmin_training_center_xml(tcx_filename)
|
34
|
+
handler = Gooby::TrainingCenterXmlParser.new
|
35
|
+
Document.parse_stream((File.new tcx_filename), handler)
|
36
|
+
handler.put_all_run_tkpt_csv(true)
|
37
|
+
end
|
38
|
+
|
28
39
|
def generate_google_map(csv_filename, options_obj)
|
29
40
|
generator = Gooby::GoogleMapGenerator.new(csv_filename)
|
30
41
|
generator.generate_page(options_obj)
|
@@ -0,0 +1,183 @@
|
|
1
|
+
module Gooby
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
Instances of this class are used to parse a Garmin TrainingCenter XML file
|
5
|
+
in a SAX-like manner. Instances of the model classes - History, Run, Track,
|
6
|
+
Trackpoint, etc. are created in this parsing process.
|
7
|
+
|
8
|
+
See http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd for the XML
|
9
|
+
Schema Definition for Garmin TrainingCenter XML.
|
10
|
+
=end
|
11
|
+
|
12
|
+
class TrainingCenterXmlParser
|
13
|
+
|
14
|
+
DETAIL_TAGS = %w( Notes StartTime Duration Length Time
|
15
|
+
TotalTimeSeconds DistanceMeters
|
16
|
+
LatitudeDegrees LongitudeDegrees AltitudeMeters BeginPosition EndPosition )
|
17
|
+
|
18
|
+
include REXML::StreamListener
|
19
|
+
|
20
|
+
attr_reader :history, :cvHash, :tagCount
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@cvHash = Hash.new("")
|
24
|
+
@tagCount = 0
|
25
|
+
@runCount = 0
|
26
|
+
@lapCount = 0
|
27
|
+
@trackCount = 0
|
28
|
+
@trackpoint_count = 0
|
29
|
+
@currText = "";
|
30
|
+
@history = History.new
|
31
|
+
@currRun = nil
|
32
|
+
@currLap = nil
|
33
|
+
@currTrack = nil
|
34
|
+
@currBeginPosition = nil
|
35
|
+
@currEndPosition = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
public
|
39
|
+
|
40
|
+
# SAX API method; handles 'Activity', 'Lap', 'Track'.
|
41
|
+
def tag_start(tagname, attrs)
|
42
|
+
@tagCount += 1
|
43
|
+
@currTag = tagname
|
44
|
+
@cvHash[tagname] = ''
|
45
|
+
|
46
|
+
if detail_tag?(tagname)
|
47
|
+
@inDetail = true
|
48
|
+
end
|
49
|
+
|
50
|
+
if is_tag?('Activity', tagname)
|
51
|
+
@runCount = @runCount + 1
|
52
|
+
@lapCount = 0
|
53
|
+
@trackCount = 0
|
54
|
+
@currRun = Run.new(@runCount)
|
55
|
+
@history.add_run(@currRun)
|
56
|
+
@cvHash['Notes'] = ''
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
if is_tag?('Lap', tagname)
|
61
|
+
@lapCount = @lapCount + 1
|
62
|
+
@currLap = Lap.new(@lapCount)
|
63
|
+
# TODO - capture value of 'StartTime' attribute.
|
64
|
+
return
|
65
|
+
end
|
66
|
+
|
67
|
+
if is_tag?('Track', tagname)
|
68
|
+
@trackCount = @trackCount + 1
|
69
|
+
@currTrack = Track.new(@trackCount)
|
70
|
+
@trackpoint_count = 0
|
71
|
+
return
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
# SAX API method; handles 'Position', 'Trackpoint', 'Track', 'Lap', 'Run'.
|
77
|
+
def tag_end(tagname)
|
78
|
+
if @inDetail
|
79
|
+
@cvHash[tagname] = @currText
|
80
|
+
else
|
81
|
+
if is_tag?('Position', tagname)
|
82
|
+
lat = @cvHash['LatitudeDegrees']
|
83
|
+
long = @cvHash['LongitudeDegrees']
|
84
|
+
@currBeginPosition = Position.new(lat.strip, long.strip, '')
|
85
|
+
@currEndPosition = Position.new(lat.strip, long.strip, '')
|
86
|
+
end
|
87
|
+
|
88
|
+
if is_tag?('BeginPosition', tagname)
|
89
|
+
lat = @cvHash['LatitudeDegrees']
|
90
|
+
long = @cvHash['LongitudeDegrees']
|
91
|
+
@currBeginPosition = Position.new(lat.strip, long.strip, '')
|
92
|
+
end
|
93
|
+
|
94
|
+
if is_tag?('EndPosition', tagname)
|
95
|
+
lat = @cvHash['LatitudeDegrees']
|
96
|
+
long = @cvHash['LongitudeDegrees']
|
97
|
+
@currEndPosition = Position.new(lat.strip, long.strip, '')
|
98
|
+
end
|
99
|
+
|
100
|
+
if is_tag?('Trackpoint', tagname)
|
101
|
+
@trackpoint_count = @trackpoint_count + 1
|
102
|
+
lat = @cvHash['LatitudeDegrees']
|
103
|
+
long = @cvHash['LongitudeDegrees']
|
104
|
+
alt = @cvHash['AltitudeMeters']
|
105
|
+
time = @cvHash['Time']
|
106
|
+
tp = Trackpoint.new(@trackpoint_count, lat, long, alt, time)
|
107
|
+
@currTrack.add_trackpoint(tp)
|
108
|
+
end
|
109
|
+
|
110
|
+
if is_tag?('Track', tagname)
|
111
|
+
if @currRun != nil
|
112
|
+
@currRun.add_track(@currTrack)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if is_tag?('Lap', tagname)
|
117
|
+
# TotalTimeSeconds DistanceMeters
|
118
|
+
# TODO - rework tnd of Lap for tcx
|
119
|
+
# @currLap.startTime = @cvHash['StartTime']
|
120
|
+
# @currLap.duration = Duration.new(@cvHash['Duration'])
|
121
|
+
# @currLap.length = @cvHash['Length']
|
122
|
+
# @currLap.beginPosition = @currBeginPosition
|
123
|
+
# @currLap.endPosition = @currEndPosition
|
124
|
+
@currRun.add_lap(@currLap)
|
125
|
+
end
|
126
|
+
|
127
|
+
if is_tag?('Activity', tagname)
|
128
|
+
@currRun.notes = @cvHash['Notes']
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
@inDetail = false
|
133
|
+
@currText = ""
|
134
|
+
@currTag = ""
|
135
|
+
end
|
136
|
+
|
137
|
+
# SAX API method.
|
138
|
+
def text(txt)
|
139
|
+
if @inDetail
|
140
|
+
@currText = @currText + txt
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Iterate all parsed Run objects and print each with to_s.
|
145
|
+
def gdump()
|
146
|
+
@history.runs().each { |run| puts run.to_s }
|
147
|
+
end
|
148
|
+
|
149
|
+
# Iterate all parsed Run objects and print each with to_s.
|
150
|
+
def dump()
|
151
|
+
@history.runs().each { |run| puts run.to_s }
|
152
|
+
end
|
153
|
+
|
154
|
+
# Iterate all parsed Run objects and print each with put_csv.
|
155
|
+
def put_run_csv()
|
156
|
+
@history.runs().each { |run| run.put_csv() }
|
157
|
+
end
|
158
|
+
|
159
|
+
# Iterate all parsed Run objects and print each with put_tkpt_csv.
|
160
|
+
def put_all_run_tkpt_csv(with_header_comment)
|
161
|
+
@history.runs.each { |run|
|
162
|
+
run.put_tkpt_csv(with_header_comment)
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def is_tag?(tagname, value)
|
169
|
+
tagname == value
|
170
|
+
end
|
171
|
+
|
172
|
+
def detail_tag?(tagname)
|
173
|
+
DETAIL_TAGS.each { |typ|
|
174
|
+
if typ == tagname
|
175
|
+
return true
|
176
|
+
end
|
177
|
+
}
|
178
|
+
return false
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
end # end of module
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Gooby
|
2
|
+
|
3
|
+
=begin rdoc
|
4
|
+
Instances of this class are used to split a large Garmin TrainingCenter
|
5
|
+
*.tcx file into individual 'activity_' files.
|
6
|
+
=end
|
7
|
+
|
8
|
+
class TrainingCenterXmlSplitter < GoobyObject
|
9
|
+
|
10
|
+
attr_reader :out_dir, :training_center_files, :out_files_hash
|
11
|
+
|
12
|
+
def initialize(tcx_file, out_dir)
|
13
|
+
@out_dir = out_dir
|
14
|
+
@training_center_files = Array.new
|
15
|
+
@training_center_files << tcx_file
|
16
|
+
@out_files_hash = Hash.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def split
|
20
|
+
@training_center_files.each { |f| process_file(f) }
|
21
|
+
write_files
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def process_file(training_center_tcx_file)
|
27
|
+
@file_name = training_center_tcx_file
|
28
|
+
@tcx_lines = read_lines(@file_name, false)
|
29
|
+
@line_num = 0
|
30
|
+
@activity_num = 0
|
31
|
+
@curr_activity_lines = Array.new
|
32
|
+
@curr_activity_tkpts = 0
|
33
|
+
@start_line_num = 0
|
34
|
+
@end_line_num = 0
|
35
|
+
@activity_start_time = nil
|
36
|
+
|
37
|
+
@tcx_lines.each { |line|
|
38
|
+
@line_num = @line_num + 1
|
39
|
+
if (line.match(/<Activity /))
|
40
|
+
@activity_num = @activity_num + 1
|
41
|
+
@start_line_num = @line_num
|
42
|
+
@curr_activity_lines = Array.new
|
43
|
+
@curr_activity_lines << line
|
44
|
+
elsif (line.match(/<Id>/)) # <Id>2007-03-03T15:58:57Z</Id> <StartTime>2007-01-13T15:37:06Z</StartTime>
|
45
|
+
@curr_activity_lines << line
|
46
|
+
if @activity_start_time == nil
|
47
|
+
clone = String.new(line)
|
48
|
+
clone.gsub!(/[<>]/, ' ')
|
49
|
+
clone.gsub!(/[-:T]/, '_')
|
50
|
+
clone.gsub!(/[Z]/, '')
|
51
|
+
tokens = clone.split
|
52
|
+
@activity_start_time = tokens[1]
|
53
|
+
end
|
54
|
+
elsif (line.match(/<Trackpoint>/))
|
55
|
+
@curr_activity_tkpts = @curr_activity_tkpts + 1
|
56
|
+
@curr_activity_lines << line
|
57
|
+
elsif (line.match(/<\/Activity/))
|
58
|
+
@end_line_num = @line_num
|
59
|
+
@curr_activity_lines << line
|
60
|
+
end_run
|
61
|
+
elsif (@curr_activity_lines.size > 0)
|
62
|
+
@curr_activity_lines << line
|
63
|
+
end
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def end_run
|
68
|
+
out_file = "#{@out_dir}/activity_#{@activity_start_time}.xml"
|
69
|
+
comment = "<!-- file: #{out_file} lines: #{@curr_activity_lines.size} (#{@start_line_num} to #{@end_line_num}) tkpts: #{@curr_activity_tkpts} --> \n"
|
70
|
+
@curr_activity_lines.insert(0, comment)
|
71
|
+
|
72
|
+
prev_entry = @out_files_hash[out_file]
|
73
|
+
if prev_entry
|
74
|
+
if (@curr_activity_lines.size >= prev_entry.size)
|
75
|
+
puts "previous entry overlaid for #{out_file}. curr=#{@curr_activity_lines.size} prev=#{prev_entry.size}"
|
76
|
+
@out_files_hash[out_file] = @curr_activity_lines
|
77
|
+
else
|
78
|
+
puts "previous entry retained for #{out_file}. curr=#{@curr_activity_lines.size} prev=#{prev_entry.size}"
|
79
|
+
end
|
80
|
+
else
|
81
|
+
puts "new entry for #{out_file}. curr=#{@curr_activity_lines.size}"
|
82
|
+
@out_files_hash[out_file] = @curr_activity_lines
|
83
|
+
end
|
84
|
+
|
85
|
+
@curr_activity_lines = Array.new
|
86
|
+
@curr_activity_tkpts = 0
|
87
|
+
@start_line_num = 0
|
88
|
+
@end_line_num = 0
|
89
|
+
@activity_start_time = nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def write_files
|
93
|
+
out_names = @out_files_hash.keys.sort
|
94
|
+
puts "Writing #{out_names.size} extract files..."
|
95
|
+
out_names.each { |out_name|
|
96
|
+
lines = @out_files_hash[out_name]
|
97
|
+
out = File.new out_name, "w+"
|
98
|
+
lines.each { |line| out.write line }
|
99
|
+
out.flush
|
100
|
+
out.close
|
101
|
+
puts "File written: #{out_name}"
|
102
|
+
}
|
103
|
+
puts "output files written."
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end # end of module
|
109
|
+
|
data/lib/gooby.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Packaged on
|
1
|
+
# Packaged on Fri Mar 09 06:03:09 EST 2007
|
2
2
|
|
3
3
|
=begin
|
4
4
|
|
@@ -34,7 +34,7 @@ module Gooby
|
|
34
34
|
|
35
35
|
# Return a String version number, like '1.0.0'.
|
36
36
|
def project_version_number
|
37
|
-
'0.9.
|
37
|
+
'0.9.4'
|
38
38
|
end
|
39
39
|
|
40
40
|
# Return a String date, like '2007/02/25'.
|
@@ -805,6 +805,293 @@ module Gooby
|
|
805
805
|
end
|
806
806
|
|
807
807
|
|
808
|
+
=begin rdoc
|
809
|
+
Instances of this class are used to parse a Garmin TrainingCenter XML file
|
810
|
+
in a SAX-like manner. Instances of the model classes - History, Run, Track,
|
811
|
+
Trackpoint, etc. are created in this parsing process.
|
812
|
+
|
813
|
+
See http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd for the XML
|
814
|
+
Schema Definition for Garmin TrainingCenter XML.
|
815
|
+
=end
|
816
|
+
|
817
|
+
class TrainingCenterXmlParser
|
818
|
+
|
819
|
+
DETAIL_TAGS = %w( Notes StartTime Duration Length Time
|
820
|
+
TotalTimeSeconds DistanceMeters
|
821
|
+
LatitudeDegrees LongitudeDegrees AltitudeMeters BeginPosition EndPosition )
|
822
|
+
|
823
|
+
include REXML::StreamListener
|
824
|
+
|
825
|
+
attr_reader :history, :cvHash, :tagCount
|
826
|
+
|
827
|
+
def initialize
|
828
|
+
@cvHash = Hash.new("")
|
829
|
+
@tagCount = 0
|
830
|
+
@runCount = 0
|
831
|
+
@lapCount = 0
|
832
|
+
@trackCount = 0
|
833
|
+
@trackpoint_count = 0
|
834
|
+
@currText = "";
|
835
|
+
@history = History.new
|
836
|
+
@currRun = nil
|
837
|
+
@currLap = nil
|
838
|
+
@currTrack = nil
|
839
|
+
@currBeginPosition = nil
|
840
|
+
@currEndPosition = nil
|
841
|
+
end
|
842
|
+
|
843
|
+
public
|
844
|
+
|
845
|
+
# SAX API method; handles 'Activity', 'Lap', 'Track'.
|
846
|
+
def tag_start(tagname, attrs)
|
847
|
+
@tagCount += 1
|
848
|
+
@currTag = tagname
|
849
|
+
@cvHash[tagname] = ''
|
850
|
+
|
851
|
+
if detail_tag?(tagname)
|
852
|
+
@inDetail = true
|
853
|
+
end
|
854
|
+
|
855
|
+
if is_tag?('Activity', tagname)
|
856
|
+
@runCount = @runCount + 1
|
857
|
+
@lapCount = 0
|
858
|
+
@trackCount = 0
|
859
|
+
@currRun = Run.new(@runCount)
|
860
|
+
@history.add_run(@currRun)
|
861
|
+
@cvHash['Notes'] = ''
|
862
|
+
return
|
863
|
+
end
|
864
|
+
|
865
|
+
if is_tag?('Lap', tagname)
|
866
|
+
@lapCount = @lapCount + 1
|
867
|
+
@currLap = Lap.new(@lapCount)
|
868
|
+
# TODO - capture value of 'StartTime' attribute.
|
869
|
+
return
|
870
|
+
end
|
871
|
+
|
872
|
+
if is_tag?('Track', tagname)
|
873
|
+
@trackCount = @trackCount + 1
|
874
|
+
@currTrack = Track.new(@trackCount)
|
875
|
+
@trackpoint_count = 0
|
876
|
+
return
|
877
|
+
end
|
878
|
+
|
879
|
+
end
|
880
|
+
|
881
|
+
# SAX API method; handles 'Position', 'Trackpoint', 'Track', 'Lap', 'Run'.
|
882
|
+
def tag_end(tagname)
|
883
|
+
if @inDetail
|
884
|
+
@cvHash[tagname] = @currText
|
885
|
+
else
|
886
|
+
if is_tag?('Position', tagname)
|
887
|
+
lat = @cvHash['LatitudeDegrees']
|
888
|
+
long = @cvHash['LongitudeDegrees']
|
889
|
+
@currBeginPosition = Position.new(lat.strip, long.strip, '')
|
890
|
+
@currEndPosition = Position.new(lat.strip, long.strip, '')
|
891
|
+
end
|
892
|
+
|
893
|
+
if is_tag?('BeginPosition', tagname)
|
894
|
+
lat = @cvHash['LatitudeDegrees']
|
895
|
+
long = @cvHash['LongitudeDegrees']
|
896
|
+
@currBeginPosition = Position.new(lat.strip, long.strip, '')
|
897
|
+
end
|
898
|
+
|
899
|
+
if is_tag?('EndPosition', tagname)
|
900
|
+
lat = @cvHash['LatitudeDegrees']
|
901
|
+
long = @cvHash['LongitudeDegrees']
|
902
|
+
@currEndPosition = Position.new(lat.strip, long.strip, '')
|
903
|
+
end
|
904
|
+
|
905
|
+
if is_tag?('Trackpoint', tagname)
|
906
|
+
@trackpoint_count = @trackpoint_count + 1
|
907
|
+
lat = @cvHash['LatitudeDegrees']
|
908
|
+
long = @cvHash['LongitudeDegrees']
|
909
|
+
alt = @cvHash['AltitudeMeters']
|
910
|
+
time = @cvHash['Time']
|
911
|
+
tp = Trackpoint.new(@trackpoint_count, lat, long, alt, time)
|
912
|
+
@currTrack.add_trackpoint(tp)
|
913
|
+
end
|
914
|
+
|
915
|
+
if is_tag?('Track', tagname)
|
916
|
+
if @currRun != nil
|
917
|
+
@currRun.add_track(@currTrack)
|
918
|
+
end
|
919
|
+
end
|
920
|
+
|
921
|
+
if is_tag?('Lap', tagname)
|
922
|
+
# TotalTimeSeconds DistanceMeters
|
923
|
+
# TODO - rework tnd of Lap for tcx
|
924
|
+
# @currLap.startTime = @cvHash['StartTime']
|
925
|
+
# @currLap.duration = Duration.new(@cvHash['Duration'])
|
926
|
+
# @currLap.length = @cvHash['Length']
|
927
|
+
# @currLap.beginPosition = @currBeginPosition
|
928
|
+
# @currLap.endPosition = @currEndPosition
|
929
|
+
@currRun.add_lap(@currLap)
|
930
|
+
end
|
931
|
+
|
932
|
+
if is_tag?('Activity', tagname)
|
933
|
+
@currRun.notes = @cvHash['Notes']
|
934
|
+
end
|
935
|
+
end
|
936
|
+
|
937
|
+
@inDetail = false
|
938
|
+
@currText = ""
|
939
|
+
@currTag = ""
|
940
|
+
end
|
941
|
+
|
942
|
+
# SAX API method.
|
943
|
+
def text(txt)
|
944
|
+
if @inDetail
|
945
|
+
@currText = @currText + txt
|
946
|
+
end
|
947
|
+
end
|
948
|
+
|
949
|
+
# Iterate all parsed Run objects and print each with to_s.
|
950
|
+
def gdump()
|
951
|
+
@history.runs().each { |run| puts run.to_s }
|
952
|
+
end
|
953
|
+
|
954
|
+
# Iterate all parsed Run objects and print each with to_s.
|
955
|
+
def dump()
|
956
|
+
@history.runs().each { |run| puts run.to_s }
|
957
|
+
end
|
958
|
+
|
959
|
+
# Iterate all parsed Run objects and print each with put_csv.
|
960
|
+
def put_run_csv()
|
961
|
+
@history.runs().each { |run| run.put_csv() }
|
962
|
+
end
|
963
|
+
|
964
|
+
# Iterate all parsed Run objects and print each with put_tkpt_csv.
|
965
|
+
def put_all_run_tkpt_csv(with_header_comment)
|
966
|
+
@history.runs.each { |run|
|
967
|
+
run.put_tkpt_csv(with_header_comment)
|
968
|
+
}
|
969
|
+
end
|
970
|
+
|
971
|
+
private
|
972
|
+
|
973
|
+
def is_tag?(tagname, value)
|
974
|
+
tagname == value
|
975
|
+
end
|
976
|
+
|
977
|
+
def detail_tag?(tagname)
|
978
|
+
DETAIL_TAGS.each { |typ|
|
979
|
+
if typ == tagname
|
980
|
+
return true
|
981
|
+
end
|
982
|
+
}
|
983
|
+
return false
|
984
|
+
end
|
985
|
+
|
986
|
+
end
|
987
|
+
|
988
|
+
|
989
|
+
=begin rdoc
|
990
|
+
Instances of this class are used to split a large Garmin TrainingCenter
|
991
|
+
*.tcx file into individual 'activity_' files.
|
992
|
+
=end
|
993
|
+
|
994
|
+
class TrainingCenterXmlSplitter < GoobyObject
|
995
|
+
|
996
|
+
attr_reader :out_dir, :training_center_files, :out_files_hash
|
997
|
+
|
998
|
+
def initialize(tcx_file, out_dir)
|
999
|
+
@out_dir = out_dir
|
1000
|
+
@training_center_files = Array.new
|
1001
|
+
@training_center_files << tcx_file
|
1002
|
+
@out_files_hash = Hash.new
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
def split
|
1006
|
+
@training_center_files.each { |f| process_file(f) }
|
1007
|
+
write_files
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
private
|
1011
|
+
|
1012
|
+
def process_file(training_center_tcx_file)
|
1013
|
+
@file_name = training_center_tcx_file
|
1014
|
+
@tcx_lines = read_lines(@file_name, false)
|
1015
|
+
@line_num = 0
|
1016
|
+
@activity_num = 0
|
1017
|
+
@curr_activity_lines = Array.new
|
1018
|
+
@curr_activity_tkpts = 0
|
1019
|
+
@start_line_num = 0
|
1020
|
+
@end_line_num = 0
|
1021
|
+
@activity_start_time = nil
|
1022
|
+
|
1023
|
+
@tcx_lines.each { |line|
|
1024
|
+
@line_num = @line_num + 1
|
1025
|
+
if (line.match(/<Activity /))
|
1026
|
+
@activity_num = @activity_num + 1
|
1027
|
+
@start_line_num = @line_num
|
1028
|
+
@curr_activity_lines = Array.new
|
1029
|
+
@curr_activity_lines << line
|
1030
|
+
elsif (line.match(/<Id>/)) # <Id>2007-03-03T15:58:57Z</Id> <StartTime>2007-01-13T15:37:06Z</StartTime>
|
1031
|
+
@curr_activity_lines << line
|
1032
|
+
if @activity_start_time == nil
|
1033
|
+
clone = String.new(line)
|
1034
|
+
clone.gsub!(/[<>]/, ' ')
|
1035
|
+
clone.gsub!(/[-:T]/, '_')
|
1036
|
+
clone.gsub!(/[Z]/, '')
|
1037
|
+
tokens = clone.split
|
1038
|
+
@activity_start_time = tokens[1]
|
1039
|
+
end
|
1040
|
+
elsif (line.match(/<Trackpoint>/))
|
1041
|
+
@curr_activity_tkpts = @curr_activity_tkpts + 1
|
1042
|
+
@curr_activity_lines << line
|
1043
|
+
elsif (line.match(/<\/Activity/))
|
1044
|
+
@end_line_num = @line_num
|
1045
|
+
@curr_activity_lines << line
|
1046
|
+
end_run
|
1047
|
+
elsif (@curr_activity_lines.size > 0)
|
1048
|
+
@curr_activity_lines << line
|
1049
|
+
end
|
1050
|
+
}
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
def end_run
|
1054
|
+
out_file = "#{@out_dir}/activity_#{@activity_start_time}.xml"
|
1055
|
+
comment = "<!-- file: #{out_file} lines: #{@curr_activity_lines.size} (#{@start_line_num} to #{@end_line_num}) tkpts: #{@curr_activity_tkpts} --> \n"
|
1056
|
+
@curr_activity_lines.insert(0, comment)
|
1057
|
+
|
1058
|
+
prev_entry = @out_files_hash[out_file]
|
1059
|
+
if prev_entry
|
1060
|
+
if (@curr_activity_lines.size >= prev_entry.size)
|
1061
|
+
puts "previous entry overlaid for #{out_file}. curr=#{@curr_activity_lines.size} prev=#{prev_entry.size}"
|
1062
|
+
@out_files_hash[out_file] = @curr_activity_lines
|
1063
|
+
else
|
1064
|
+
puts "previous entry retained for #{out_file}. curr=#{@curr_activity_lines.size} prev=#{prev_entry.size}"
|
1065
|
+
end
|
1066
|
+
else
|
1067
|
+
puts "new entry for #{out_file}. curr=#{@curr_activity_lines.size}"
|
1068
|
+
@out_files_hash[out_file] = @curr_activity_lines
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
@curr_activity_lines = Array.new
|
1072
|
+
@curr_activity_tkpts = 0
|
1073
|
+
@start_line_num = 0
|
1074
|
+
@end_line_num = 0
|
1075
|
+
@activity_start_time = nil
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
def write_files
|
1079
|
+
out_names = @out_files_hash.keys.sort
|
1080
|
+
puts "Writing #{out_names.size} extract files..."
|
1081
|
+
out_names.each { |out_name|
|
1082
|
+
lines = @out_files_hash[out_name]
|
1083
|
+
out = File.new out_name, "w+"
|
1084
|
+
lines.each { |line| out.write line }
|
1085
|
+
out.flush
|
1086
|
+
out.close
|
1087
|
+
puts "File written: #{out_name}"
|
1088
|
+
}
|
1089
|
+
puts "output files written."
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
|
808
1095
|
=begin rdoc
|
809
1096
|
Instances of this class represent a the set of Geographic data defined in file geo.txt
|
810
1097
|
=end
|
@@ -2244,17 +2531,28 @@ HERE
|
|
2244
2531
|
Gooby::Options.new(yaml_filename)
|
2245
2532
|
end
|
2246
2533
|
|
2247
|
-
def
|
2534
|
+
def split_garmin_forerunner_logbook_xml(xml_filename, out_dir)
|
2248
2535
|
splitter = Gooby::ForerunnerXmlSplitter.new(xml_filename, out_dir)
|
2249
2536
|
splitter.split
|
2250
2537
|
end
|
2251
2538
|
|
2252
|
-
def
|
2539
|
+
def split_garmin_training_center_xml(tcx_filename, out_dir)
|
2540
|
+
splitter = Gooby::TrainingCenterXmlSplitter.new(tcx_filename, out_dir)
|
2541
|
+
splitter.split
|
2542
|
+
end
|
2543
|
+
|
2544
|
+
def parse_garmin_forerunner_logbook_xml(xml_filename)
|
2253
2545
|
handler = Gooby::ForerunnerXmlParser.new
|
2254
2546
|
Document.parse_stream((File.new xml_filename), handler)
|
2255
2547
|
handler.put_all_run_tkpt_csv(true)
|
2256
2548
|
end
|
2257
|
-
|
2549
|
+
|
2550
|
+
def parse_garmin_training_center_xml(tcx_filename)
|
2551
|
+
handler = Gooby::TrainingCenterXmlParser.new
|
2552
|
+
Document.parse_stream((File.new tcx_filename), handler)
|
2553
|
+
handler.put_all_run_tkpt_csv(true)
|
2554
|
+
end
|
2555
|
+
|
2258
2556
|
def generate_google_map(csv_filename, options_obj)
|
2259
2557
|
generator = Gooby::GoogleMapGenerator.new(csv_filename)
|
2260
2558
|
generator.generate_page(options_obj)
|
data/pkg/pkg.rb
CHANGED
@@ -60,7 +60,7 @@ class TestModuleProjectInfo < Test::Unit::TestCase
|
|
60
60
|
def test_module_GoobyProjectInfo_project_version_number
|
61
61
|
|
62
62
|
obj = Gooby::GoobyObject.new
|
63
|
-
assert_equal '0.9.
|
63
|
+
assert_equal '0.9.4', obj.project_version_number
|
64
64
|
end
|
65
65
|
|
66
66
|
def test_module_GoobyProjectInfo_project_year
|