gooby 1.0.0 → 1.1.0

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.
Files changed (61) hide show
  1. data/README +144 -180
  2. data/bin/example_usage.txt +55 -0
  3. data/bin/gooby_been_there.rb +35 -0
  4. data/bin/gooby_config.rb +16 -0
  5. data/bin/gooby_gen_gmap.rb +16 -0
  6. data/bin/gooby_parser.rb +19 -0
  7. data/bin/gooby_splitter.rb +18 -0
  8. data/bin/gooby_version.rb +16 -0
  9. data/bin/run_all.sh +23 -0
  10. data/bin/run_been_there.sh +16 -0
  11. data/bin/run_db_gen.sh +11 -0
  12. data/bin/run_db_load.sh +11 -0
  13. data/bin/run_gen_gmaps.sh +20 -0
  14. data/bin/run_parse.sh +43 -0
  15. data/bin/run_parse_named.sh +19 -0
  16. data/bin/run_split.sh +23 -0
  17. data/config/gooby_config.yaml +137 -0
  18. data/data/20050305_corporate_cup_hm.csv +251 -251
  19. data/data/20050430_nashville_marathon.csv +1208 -1208
  20. data/data/20060115_phoenix_marathon.csv +1280 -1280
  21. data/data/davidson_11m_20070101.csv +251 -0
  22. data/data/davidson_11m_20070101.xml +2020 -0
  23. data/data/davidson_5K_20070505.csv +286 -0
  24. data/data/davidson_5K_20070505.xml +2875 -0
  25. data/lib/gooby.rb +889 -361
  26. data/samples/20050305_corporate_cup_hm.html +270 -270
  27. data/samples/20050430_nashville_marathon.html +1240 -1240
  28. data/samples/20060115_phoenix_marathon.html +1312 -1312
  29. data/samples/been_there.txt +744 -0
  30. data/samples/davidson_11m_20070101.html +432 -0
  31. data/samples/davidson_5K_20070505.html +395 -0
  32. data/sql/gooby.ddl +56 -0
  33. data/sql/gooby_load.dml +35 -0
  34. metadata +30 -32
  35. data/bin/20050305_corporate_cup_hm_to_csv.rb +0 -9
  36. data/bin/20050430_nashville_marathon_to_csv.rb +0 -9
  37. data/bin/20060115_phoenix_marathon_to_csv.rb +0 -9
  38. data/bin/activity_2007_03_10_13_02_32.xml_to_csv.rb +0 -9
  39. data/bin/example_usage.rb +0 -46
  40. data/bin/example_usage.sh +0 -52
  41. data/bin/gen_gap_phx.rb +0 -10
  42. data/bin/gen_gmap_cc_2005.rb +0 -10
  43. data/bin/gen_gmap_cc_2007.rb +0 -10
  44. data/bin/gen_gmap_nashville.rb +0 -10
  45. data/bin/gen_gmap_phx.rb +0 -10
  46. data/bin/phx_to_csv.rb +0 -47
  47. data/bin/split_forerunner_logbook_2007.rb +0 -10
  48. data/bin/split_training_center_2007.rb +0 -8
  49. data/data/2007_03_10.tcx +0 -28445
  50. data/data/activity_2007_03_10_13_02_32.csv +0 -1168
  51. data/data/activity_2007_03_10_13_02_32.xml +0 -11695
  52. data/data/forerunner_2007.xml +0 -31872
  53. data/data/geo_data.txt +0 -171
  54. data/pkg/code_header.txt +0 -21
  55. data/pkg/pkg.rb +0 -238
  56. data/pkg/test_header.txt +0 -19
  57. data/samples/20070310_corporate_cup_hm.html +0 -1367
  58. data/samples/gps_point_capture.html +0 -54
  59. data/samples/phoenix_marathon.html +0 -1596
  60. data/tests/ts_gooby.rb +0 -1109
  61. data/tests/ts_gooby_min.rb +0 -550
data/lib/gooby.rb CHANGED
@@ -6,36 +6,41 @@ This file contains the classes and modules for the Gooby project.
6
6
  See file 'tests/ts_gooby.rb' for the regression test suite.
7
7
 
8
8
  Index of Modules and Classes in this file:
9
- line type Module or Class name
10
- ---- ------ ---------------------------
11
- 51 module Gooby
12
- 58 module GoobyKernel
13
- 185 module TestHelper
14
- 203 class GoobyObject
15
- 216 class CounterHash
16
- 299 class DelimLine
17
- 334 class DtTm
18
- 411 class Duration
19
- 490 class ForerunnerXmlParser
20
- 661 class ForerunnerXmlSplitter
21
- 771 class TrainingCenterXmlParser
22
- 948 class TrainingCenterXmlSplitter
23
- 1053 class GeoData
24
- 1236 class GoogleMapGenerator
25
- 1605 class History
26
- 1636 class Lap
27
- 1651 class Line
28
- 1721 class Options
29
- 1796 class Position
30
- 1840 class Run
31
- 2029 class SimpleXmlParser
32
- 2069 class Track
33
- 2116 class Trackpoint
34
- 2305 class CodeScanner
35
- 2597 class GoobyCommand
9
+ line type Module or Class name
10
+ ---- ------ ---------------------------
11
+ 57 module Gooby
12
+ 64 module GoobyKernel
13
+ 191 module TestHelper
14
+ 209 class GoobyObject
15
+ 222 class CounterHash
16
+ 305 class DelimLine
17
+ 340 class DtTm
18
+ 417 class Duration
19
+ 496 class ForerunnerXmlParser
20
+ 665 class ForerunnerXmlSplitter
21
+ 775 class TrainingCenterXmlParser
22
+ 962 class TrainingCenterXmlSplitter
23
+ 1067 class GeoData
24
+ 1250 class GoogleMapGenerator
25
+ 1625 class History
26
+ 1656 class Lap
27
+ 1671 class Line
28
+ 1749 class Configuration
29
+ 1848 class Point
30
+ 1987 class CsvPoint
31
+ 2042 class CvsRun
32
+ 2060 class CsvReader
33
+ 2121 class Trackpoint
34
+ 2319 class Run
35
+ 2521 class SimpleXmlParser
36
+ 2561 class Track
37
+ 2593 class Course
38
+ 2713 class CodeScanner
39
+ 3003 class GoobyCommand
36
40
 
37
41
  Gooby - Copyright 2007 by Chris Joakim.
38
42
  Gooby is available under GNU General Public License (GPL) license.
43
+
39
44
  =end
40
45
 
41
46
  require 'date'
@@ -43,6 +48,7 @@ require 'find'
43
48
  require 'rbconfig'
44
49
  require 'rexml/document'
45
50
  require 'rexml/streamlistener'
51
+ require 'singleton'
46
52
  require 'time'
47
53
  require 'yaml'
48
54
 
@@ -57,19 +63,19 @@ module Gooby
57
63
 
58
64
  module GoobyKernel
59
65
 
60
- # Return a String version number, like '1.0.0'.
66
+ # Return a String version number, like '1.1.0'.
61
67
 
62
68
  def project_name
63
69
  'Gooby'
64
70
  end
65
71
 
66
72
  def project_version_number
67
- '1.0.0'
73
+ '1.1.0'
68
74
  end
69
75
 
70
76
  # Return a String date, like '2007/03/21'.
71
77
  def project_date
72
- '2007/03/21'
78
+ '2007/06/10'
73
79
  end
74
80
 
75
81
  # Return a String containing the project author name.
@@ -372,10 +378,10 @@ module Gooby
372
378
  @time.strftime("%Y-%m-%d")
373
379
  end
374
380
 
375
- def yyyy_mm_dd_hh_mm_ss
376
- @time.strftime("%Y-%m-%d %H:%M:%S")
381
+ def yyyy_mm_dd_hh_mm_ss(delim=' ')
382
+ @time.strftime("%Y-%m-%d#{delim}%H:%M:%S")
377
383
  end
378
-
384
+
379
385
  def hh_mm_ss
380
386
  @time.strftime("%H:%M:%S")
381
387
  end
@@ -386,7 +392,7 @@ module Gooby
386
392
  t = @time - (anotherDtTm.to_i)
387
393
  t.strftime("%H:%M:%S")
388
394
  else
389
- '??:??:??'
395
+ '00:00:00'
390
396
  end
391
397
  end
392
398
 
@@ -527,6 +533,7 @@ module Gooby
527
533
  @run_count = @run_count + 1
528
534
  @lap_count = 0
529
535
  @track_count = 0
536
+ @trackpoint_count = 0
530
537
  @curr_run = Run.new(@run_count)
531
538
  @history.add_run(@curr_run)
532
539
  @cv_hash['Notes'] = ''
@@ -542,7 +549,6 @@ module Gooby
542
549
  if is_tag?('Track', tagname)
543
550
  @track_count = @track_count + 1
544
551
  @curr_track = Track.new(@track_count)
545
- @trackpoint_count = 0
546
552
  return
547
553
  end
548
554
  end
@@ -555,20 +561,20 @@ module Gooby
555
561
  if is_tag?('Position', tagname)
556
562
  lat = @cv_hash['Latitude']
557
563
  long = @cv_hash['Longitude']
558
- @curr_begin_position = Position.new(lat.strip, long.strip, '')
559
- @@curr_end_position = Position.new(lat.strip, long.strip, '')
564
+ @curr_begin_position = Point.new(lat.strip, long.strip)
565
+ @@curr_end_position = Point.new(lat.strip, long.strip)
560
566
  end
561
567
 
562
568
  if is_tag?('BeginPosition', tagname)
563
569
  lat = @cv_hash['Latitude']
564
570
  long = @cv_hash['Longitude']
565
- @curr_begin_position = Position.new(lat.strip, long.strip, '')
571
+ @curr_begin_position = Point.new(lat.strip, long.strip)
566
572
  end
567
573
 
568
574
  if is_tag?('EndPosition', tagname)
569
575
  lat = @cv_hash['Latitude']
570
576
  long = @cv_hash['Longitude']
571
- @@curr_end_position = Position.new(lat.strip, long.strip, '')
577
+ @@curr_end_position = Point.new(lat.strip, long.strip)
572
578
  end
573
579
 
574
580
  if is_tag?('Trackpoint', tagname)
@@ -576,7 +582,7 @@ module Gooby
576
582
  lat = @cv_hash['Latitude']
577
583
  long = @cv_hash['Longitude']
578
584
  alt = @cv_hash['Altitude']
579
- time = @cv_hash['Time']
585
+ time = @cv_hash['Time']
580
586
  tp = Trackpoint.new(@trackpoint_count, lat, long, alt, time)
581
587
  @curr_track.add_trackpoint(tp)
582
588
  end
@@ -629,10 +635,8 @@ module Gooby
629
635
  end
630
636
 
631
637
  # Iterate all parsed Run objects and print each with put_tkpt_csv.
632
- def put_all_run_tkpt_csv(with_header_comment)
633
- @history.runs.each { |run|
634
- run.put_tkpt_csv(with_header_comment)
635
- }
638
+ def put_all_run_tkpt_csv()
639
+ @history.runs.each { |run| run.put_tkpt_csv() }
636
640
  end
637
641
 
638
642
  private
@@ -760,7 +764,7 @@ module Gooby
760
764
  # =============================================================================
761
765
 
762
766
  =begin rdoc
763
- Instances of this class are used to parse a Garmin TrainingCenter XML file
767
+ Instances of this class are used to parse a Garmin TrainingCenter XML(TCX) file
764
768
  in a SAX-like manner. Instances of the model classes - History, Run, Track,
765
769
  Trackpoint, etc. are created in this parsing process.
766
770
 
@@ -779,19 +783,21 @@ module Gooby
779
783
  attr_reader :history, :cvHash, :tagCount
780
784
 
781
785
  def initialize
782
- @cv_hash = Hash.new("")
783
- @tag_count = 0
784
- @run_count = 0
785
- @lap_count = 0
786
- @track_count = 0
787
- @trackpoint_count = 0
788
- @curr_text = "";
789
- @history = History.new
790
- @curr_run = nil
791
- @curr_lap = nil
792
- @curr_track = nil
793
- @curr_begin_position = nil
794
- @@curr_end_position = nil
786
+ @cv_hash = Hash.new("")
787
+ @tag_count = 0
788
+ @run_count = 0
789
+ @lap_count = 0
790
+ @track_count = 0
791
+ @trackpoint_count = 0
792
+ @curr_text = "";
793
+ @history = History.new
794
+ @curr_run = nil
795
+ @curr_lap = nil
796
+ @curr_track = nil
797
+ @curr_begin_position = nil
798
+ @@curr_end_position = nil
799
+ @first_lap_start_time = nil
800
+ @curr_lap_start_time = ''
795
801
  end
796
802
 
797
803
  public
@@ -810,6 +816,7 @@ module Gooby
810
816
  @run_count = @run_count + 1
811
817
  @lap_count = 0
812
818
  @track_count = 0
819
+ @trackpoint_count = 0
813
820
  @curr_run = Run.new(@run_count)
814
821
  @history.add_run(@curr_run)
815
822
  @cv_hash['Notes'] = ''
@@ -817,16 +824,26 @@ module Gooby
817
824
  end
818
825
 
819
826
  if is_tag?('Lap', tagname)
820
- @lap_count = @lap_count + 1
827
+ @lap_count = @lap_count + 1
821
828
  @curr_lap = Lap.new(@lap_count)
822
- # TODO - capture value of 'StartTime' attribute.
829
+
830
+ attrs.each { |attr|
831
+ name = attr[0]
832
+ val = attr[1]
833
+ if (name && (name == 'StartTime'))
834
+ if (@first_lap_start_time == nil)
835
+ @first_lap_start_time = "#{val}"
836
+ end
837
+ @curr_lap_start_time = "#{val}"
838
+ end
839
+ }
840
+ # TODO - capture value of 'StartTime' attribute.
823
841
  return
824
842
  end
825
843
 
826
844
  if is_tag?('Track', tagname)
827
845
  @track_count = @track_count + 1
828
846
  @curr_track = Track.new(@track_count)
829
- @trackpoint_count = 0
830
847
  return
831
848
  end
832
849
 
@@ -840,20 +857,20 @@ module Gooby
840
857
  if is_tag?('Position', tagname)
841
858
  lat = @cv_hash['LatitudeDegrees']
842
859
  long = @cv_hash['LongitudeDegrees']
843
- @curr_begin_position = Position.new(lat.strip, long.strip, '')
844
- @@curr_end_position = Position.new(lat.strip, long.strip, '')
860
+ @curr_begin_position = Point.new(lat.strip, long.strip)
861
+ @@curr_end_position = Point.new(lat.strip, long.strip)
845
862
  end
846
863
 
847
864
  if is_tag?('BeginPosition', tagname)
848
865
  lat = @cv_hash['LatitudeDegrees']
849
866
  long = @cv_hash['LongitudeDegrees']
850
- @curr_begin_position = Position.new(lat.strip, long.strip, '')
867
+ @curr_begin_position = Point.new(lat.strip, long.strip)
851
868
  end
852
869
 
853
870
  if is_tag?('EndPosition', tagname)
854
871
  lat = @cv_hash['LatitudeDegrees']
855
872
  long = @cv_hash['LongitudeDegrees']
856
- @@curr_end_position = Position.new(lat.strip, long.strip, '')
873
+ @@curr_end_position = Point.new(lat.strip, long.strip)
857
874
  end
858
875
 
859
876
  if is_tag?('Trackpoint', tagname)
@@ -861,8 +878,14 @@ module Gooby
861
878
  lat = @cv_hash['LatitudeDegrees']
862
879
  long = @cv_hash['LongitudeDegrees']
863
880
  alt = @cv_hash['AltitudeMeters']
864
- time = @cv_hash['Time']
865
- tp = Trackpoint.new(@trackpoint_count, lat, long, alt, time)
881
+ time = @cv_hash['Time']
882
+
883
+ hash = Hash.new('')
884
+ hash['lap_number'] = "#{@lap_count}"
885
+ hash['first_lap_start_time'] = "#{@first_lap_start_time}"
886
+ hash['curr_lap_start_time'] = "#{@curr_lap_start_time}"
887
+
888
+ tp = Trackpoint.new(@trackpoint_count, lat, long, alt, time, hash)
866
889
  @curr_track.add_trackpoint(tp)
867
890
  end
868
891
 
@@ -872,14 +895,7 @@ module Gooby
872
895
  end
873
896
  end
874
897
 
875
- if is_tag?('Lap', tagname)
876
- # TotalTimeSeconds DistanceMeters
877
- # TODO - rework tnd of Lap for tcx
878
- # @curr_lap.startTime = @cv_hash['StartTime']
879
- # @curr_lap.duration = Duration.new(@cv_hash['Duration'])
880
- # @curr_lap.length = @cv_hash['Length']
881
- # @curr_lap.begin_position = @curr_begin_position
882
- # @curr_lap.end_position = @@curr_end_position
898
+ if is_tag?('Lap', tagname)
883
899
  @curr_run.add_lap(@curr_lap)
884
900
  end
885
901
 
@@ -916,10 +932,8 @@ module Gooby
916
932
  end
917
933
 
918
934
  # Iterate all parsed Run objects and print each with put_tkpt_csv.
919
- def put_all_run_tkpt_csv(with_header_comment)
920
- @history.runs.each { |run|
921
- run.put_tkpt_csv(with_header_comment)
922
- }
935
+ def put_all_run_tkpt_csv()
936
+ @history.runs.each { |run| run.put_tkpt_csv() }
923
937
  end
924
938
 
925
939
  private
@@ -1243,7 +1257,10 @@ module Gooby
1243
1257
  # 1 | 2006-01-15T18:31:10Z | 1279 | 33.42601 | -111.92927 | 347.654 | 26.3514930151813
1244
1258
  # 1 | 2004-11-13T13:05:20Z | 2 | 37.54318 | -77.43636 | -58.022 | 0.00297286231747969
1245
1259
 
1246
- def initialize(csv_file, dttm_idx=1, num_idx=2, lat_idx=3, lng_idx=4, alt_idx=5, cdist_idx=6)
1260
+ # primary_key|run_id|date|time|tkpt_num|latitude|longitude|altitude_ft|run_distance|run_elapsed|lap_tkpt_number|lap_distance|lap_elapsed
1261
+ # 2005-03-05T13:00:29Z.2|2005-03-05T13:00:29Z|2005-03-05|13:00:49|2|35.22054|-80.84506|738.4161312|0.046918021941152|00:00:20|2|0.046918021941152|00:00:20
1262
+
1263
+ def initialize(csv_file, dttm_idx=1, num_idx=4, lat_idx=5, lng_idx=6, alt_idx=7, cdist_idx=8)
1247
1264
  @csv_file = csv_file
1248
1265
  @dttm_idx = dttm_idx
1249
1266
  @num_idx = num_idx
@@ -1251,30 +1268,31 @@ module Gooby
1251
1268
  @lng_idx = lng_idx
1252
1269
  @alt_idx = alt_idx
1253
1270
  @cdist_idx = cdist_idx
1254
-
1255
- # Override default csv value indices if specified in the options yaml file.
1256
- @options = Gooby::Options.new(nil)
1257
- @dttm_idx = @options.get('csv_dttm_idx') if @options.get('csv_dttm_idx')
1258
- @num_idx = @options.get('csv_num_idx') if @options.get('csv_num_idx')
1259
- @lat_idx = @options.get('csv_lat_idx') if @options.get('csv_lat_idx')
1260
- @lng_idx = @options.get('csv_lng_idx') if @options.get('csv_lng_idx')
1261
- @alt_idx = @options.get('csv_alt_idx') if @options.get('csv_alt_idx')
1262
- @title = @options.get("#{@csv_file}")
1271
+
1272
+ # Override default csv value indices if specified in the configuration yaml file.
1273
+ @configuration = Gooby::Configuration.get_config
1274
+ @dttm_idx = @configuration.get('csv_dttm_idx') if @configuration.get('csv_dttm_idx')
1275
+ @num_idx = @configuration.get('csv_num_idx') if @configuration.get('csv_num_idx')
1276
+ @lat_idx = @configuration.get('csv_lat_idx') if @configuration.get('csv_lat_idx')
1277
+ @lng_idx = @configuration.get('csv_lng_idx') if @configuration.get('csv_lng_idx')
1278
+ @alt_idx = @configuration.get('csv_alt_idx') if @configuration.get('csv_alt_idx')
1279
+ @title = @configuration.get("#{@csv_file}")
1263
1280
 
1264
1281
  @content_hash = Hash.new('')
1265
1282
  @run = Gooby::Run.new(1)
1266
1283
  @track = Gooby::Track.new(1)
1267
1284
  @run.add_track(@track)
1268
1285
  @tkpts = Array.new
1269
- @icon_url_base = @options.get('gmap_icon_url_base')
1270
- @csv_lines = read_as_ascii_lines(@csv_file, 10, true)
1271
- @csv_lines.each { | line |
1272
- dline = Gooby::DelimLine.new(line)
1273
- if (!dline.is_comment?)
1274
- tkpt = dline.as_trackpoint(@num_idx, @lat_idx, @lng_idx, @alt_idx, @dttm_idx)
1275
- if tkpt
1276
- @track.add_trackpoint(tkpt)
1277
- end
1286
+ @icon_url_base = @configuration.get('gmap_icon_url_base')
1287
+
1288
+ list = Array.new
1289
+ list << @csv_file
1290
+ @cvs_reader = Gooby::CsvReader.new(list)
1291
+ @cvs_points = @cvs_reader.read
1292
+ @cvs_points.each { |cvs_point|
1293
+ tkpt = cvs_point.as_trackpoint
1294
+ if tkpt
1295
+ @track.add_trackpoint(tkpt)
1278
1296
  end
1279
1297
  }
1280
1298
  @run.finish
@@ -1283,15 +1301,15 @@ module Gooby
1283
1301
  =begin
1284
1302
  Returns a Hash with specific generated content at the following keys:
1285
1303
  =end
1286
- def generate(options)
1287
- if (options == nil)
1288
- @options = Gooby::Options.new(nil)
1304
+ def generate(configuration)
1305
+ if (configuration == nil)
1306
+ @configuration = Gooby::Configuration.get_config
1289
1307
  else
1290
- @options = options
1308
+ @configuration = configuration
1291
1309
  end
1292
1310
  @content_hash['when_generated'] = Time.now
1293
1311
  @content_hash['title'] = @title
1294
- @icon_url_base = @options.get('gmap_icon_url_base')
1312
+ @icon_url_base = @configuration.get('gmap_icon_url_base')
1295
1313
  filter_trackpoints
1296
1314
  compute_center_point
1297
1315
  generate_key_js
@@ -1307,8 +1325,8 @@ Returns a Hash with specific generated content at the following keys:
1307
1325
 
1308
1326
  def filter_trackpoints
1309
1327
  count, @tkpts = 0, Array.new
1310
- firstTkpt = @options.get('gmap_first_tkpt_number')
1311
- lastTkpt = @options.get('gmap_last_tkpt_number')
1328
+ firstTkpt = @configuration.get('gmap_first_tkpt_number')
1329
+ lastTkpt = @configuration.get('gmap_last_tkpt_number')
1312
1330
  @run.tracks.each { |trk|
1313
1331
  trk.trackpoints.each { |tkpt|
1314
1332
  count = count + 1
@@ -1322,7 +1340,7 @@ Returns a Hash with specific generated content at the following keys:
1322
1340
  =begin
1323
1341
  Returns a Hash with specific generated content at the following keys:
1324
1342
  =end
1325
- def generate_page(options)
1343
+ def generate_page(configuration)
1326
1344
 
1327
1345
  # puts "generate_page #{@csv_file} #{@csv_lines.size}"
1328
1346
  content_hash = generate(nil)
@@ -1381,18 +1399,19 @@ HERE
1381
1399
  end
1382
1400
 
1383
1401
  def generate_key_js
1384
- key = @options.get('gmap_key')
1402
+ key = @configuration.get('gmap_key')
1385
1403
  key.strip!
1386
- s = '<script src="http://maps.google.com/maps?file=api&v=2&key='
1404
+ # <script src="http://maps.google.com/maps?file=api&v=2&key=<%= @gmap_key -%>" type="text/javascript"></script>
1405
+ s = "<script src='http://maps.google.com/maps?file=api&v=2&key="
1387
1406
  s << key
1388
- s << '" type="text/javascript"></script>'
1407
+ s << "' type='text/javascript'></script>"
1389
1408
  @content_hash['key_js'] = s
1390
1409
  end
1391
1410
 
1392
1411
  def generate_map_div
1393
- width = @options.get('gmap_width')
1394
- height = @options.get('gmap_height')
1395
- id = @options.get('gmap_map_element_id')
1412
+ width = @configuration.get('gmap_width')
1413
+ height = @configuration.get('gmap_height')
1414
+ id = @configuration.get('gmap_map_element_id')
1396
1415
  s = '<div id="'
1397
1416
  s << id
1398
1417
  s << '" style="width: '
@@ -1411,13 +1430,13 @@ HERE
1411
1430
  end
1412
1431
 
1413
1432
  def generate_main_js_start
1414
- id = @options.get('gmap_map_element_id')
1415
- size = @options.get('gmap_size_control')
1416
- type = @options.get('gmap_type')
1417
- zoom = @options.get('gmap_zoom_level')
1418
- title = @options.get("#{@csv_file}")
1433
+ id = @configuration.get('gmap_map_element_id')
1434
+ size = @configuration.get('gmap_size_control')
1435
+ type = @configuration.get('gmap_type')
1436
+ zoom = @configuration.get('gmap_zoom_level')
1437
+ title = @configuration.get("#{@csv_file}")
1419
1438
  title = '' if title == nil
1420
- zoom_tab = @options.get('gmap_zoom_tab')
1439
+ zoom_tab = @configuration.get('gmap_zoom_tab')
1421
1440
  if size
1422
1441
  if size == 'smallmap'
1423
1442
  size = 'GSmallMapControl'
@@ -1474,8 +1493,8 @@ HERE
1474
1493
 
1475
1494
  def generate_main_js_route_overlay
1476
1495
  tkpt_count = @tkpts.size.to_f
1477
- app_max = @options.get('gmap_approx_max_points').to_f
1478
- gen_comments = @options.get('gmap_gen_comments')
1496
+ app_max = @configuration.get('gmap_approx_max_points').to_f
1497
+ gen_comments = @configuration.get('gmap_gen_comments')
1479
1498
  ratio = tkpt_count / app_max
1480
1499
  @start_dttm = nil
1481
1500
  if ratio > 1.0
@@ -1489,7 +1508,8 @@ HERE
1489
1508
  curr_idx = curr_idx + 1
1490
1509
  if curr_idx == 0
1491
1510
  @start_dttm = tkpt.dttm
1492
- @start_pos = tkpt.position
1511
+ @start_pos = tkpt.point
1512
+
1493
1513
  time = Time.parse(@start_dttm.dateTime().to_s)
1494
1514
  end
1495
1515
  if ((curr_idx == next_idx) || (curr_idx == last_idx) || (tkpt.is_split()))
@@ -1717,28 +1737,42 @@ HERE
1717
1737
  end
1718
1738
 
1719
1739
  # =============================================================================
1740
+ =begin
1720
1741
 
1721
- class Options < GoobyObject
1722
-
1723
- attr_reader :yaml_filename, :options
1742
+ This is a singleton class whose values are loaded from a YAML file when your
1743
+ GoobyCommand class is created. The default filename is 'gooby_config.yaml"'.
1744
+
1745
+ The YAML file contains configuration parameters, such as your Google Map key,
1746
+ map HTML options, points of interest, and courses.
1747
+
1748
+ =end
1749
+ class Configuration < GoobyObject
1750
+
1751
+ @@singleton_instance = nil
1752
+
1753
+ attr_reader :yaml_filename, :configuration
1724
1754
 
1725
- def initialize(filename) # Constructor.
1726
- filename ? @yaml_filename = filename : @yaml_filename = 'gooby_options.yaml'
1727
- loadFile()
1755
+ private_class_method :new
1756
+
1757
+ def Configuration.init(yaml_filename='gooby_config.yaml')
1758
+ return @@singleton_instance if @@singleton_instance
1759
+ @@singleton_instance = new(yaml_filename)
1728
1760
  end
1729
-
1730
- # Load the @yaml_filename
1731
- def loadFile
1732
- File.open("#{@yaml_filename}") { |fn|
1733
- @options = YAML::load(fn)
1734
- }
1761
+
1762
+ def self.get_config
1763
+ @@singleton_instance
1764
+ end
1765
+
1766
+ def initialize(yaml_filename)
1767
+ @yaml_filename = yaml_filename
1768
+ File.open("#{@yaml_filename}") { |fn| @configuration = YAML::load(fn) }
1735
1769
  end
1736
1770
 
1737
1771
  def get(name)
1738
1772
  if name == nil
1739
1773
  return ''
1740
1774
  end
1741
- s = @options["#{name}"]
1775
+ s = @configuration["#{name}"]
1742
1776
 
1743
1777
  # Provide "sensible defaults".
1744
1778
  if s == nil
@@ -1774,44 +1808,102 @@ HERE
1774
1808
  end
1775
1809
  s
1776
1810
  end
1811
+
1812
+ def print_all
1813
+ @configuration.keys.sort.each { |key| puts "#{key}: #{@configuration["#{key}"]}" }
1814
+ end
1815
+
1816
+ def print_all_poi
1817
+ @configuration.keys.sort.each { |key|
1818
+ if (key.match(/poi[\.]/))
1819
+ val = @configuration["#{key}"]
1820
+ poi = Point.new(val)
1821
+ puts poi.to_s
1822
+ end
1823
+ }
1824
+ end
1825
+
1826
+ def get_poi(number)
1827
+ val = @configuration["poi.#{number}"]
1828
+ (val) ? Point.new(val) : nil
1829
+ end
1830
+
1831
+ def get_course(number)
1832
+ val = @configuration["course.#{number}"]
1833
+ (val) ? Course.new(val) : nil
1834
+ end
1777
1835
 
1778
1836
  def size
1779
- @options.size
1837
+ @configuration.size
1780
1838
  end
1781
1839
 
1782
1840
  # Return a String containing yaml filename and entry count.
1783
1841
  def to_s
1784
- return "Options: filename: #{@yaml_filename} entries: #{@options.size}"
1842
+ return "# Configuration: filename: #{@yaml_filename} entries: #{@configuration.size}"
1785
1843
  end
1786
1844
  end
1787
1845
 
1788
1846
  # =============================================================================
1789
-
1790
- =begin rdoc
1791
- Instances of this class represent a <Position> aggregate object from a
1792
- Forerunner XML file. Each contains a latitude and longitude.
1793
- Instances within a Trackpoint will also contain an altitude.
1794
- =end
1795
1847
 
1796
- class Position < GoobyObject
1848
+ class Point < GoobyObject
1797
1849
 
1798
- attr_accessor :latitude, :longitude, :altitude, :note
1850
+ attr_accessor :number, :latitude, :longitude, :altitude, :note
1799
1851
 
1800
- def initialize(lat, lng, alt='0', note='')
1801
- @latitude = lat.to_s
1802
- @longitude = lng.to_s
1803
- @altitude = alt.to_s
1804
- @note = note.to_s
1852
+ def initialize(*args)
1853
+ @number, @latitude, @longitude, @altitude, @note = '', '', '', '', ''
1854
+ if args
1855
+ if args.size == 1
1856
+ initialize_from_string(args[0]) # yaml
1857
+ else
1858
+ initialize_from_array(args)
1859
+ end
1860
+ end
1805
1861
  end
1806
-
1862
+
1863
+ def csv_delim
1864
+ '|'
1865
+ end
1866
+
1867
+ def initialize_from_array(args)
1868
+ @latitude = args[0] if args.size > 0
1869
+ @longitude = args[1] if args.size > 1
1870
+ @altitude = args[2] if args.size > 2
1871
+ @note = args[3] if args.size > 3
1872
+ end
1873
+
1874
+ def initialize_from_string(yaml_value_string)
1875
+ tokens = yaml_value_string.split
1876
+ if (tokens.size > 2)
1877
+ @latitude = tokens[0]
1878
+ @longitude = tokens[1]
1879
+ @note = ''
1880
+ count = 0
1881
+ tokens.each { |tok|
1882
+ count = count + 1
1883
+ if (count > 2)
1884
+ @note << tok
1885
+ @note << ' '
1886
+ end
1887
+ }
1888
+ end
1889
+ end
1890
+
1807
1891
  public
1808
1892
 
1809
1893
  def to_s
1810
1894
  return "lat: #{@latitude} lng: #{@longitude} alt: #{@altitude} note: #{@note}"
1811
1895
  end
1896
+
1897
+ def to_formatted_string
1898
+ s = "lat: #{@latitude.to_s.ljust(20)}"
1899
+ s << " lng: #{@longitude.to_s.ljust(20)}"
1900
+ s << " poi.#{@number.to_s.ljust(12)}" if @number
1901
+ s << " #{@note}" if @note
1902
+ s
1903
+ end
1812
1904
 
1813
1905
  def to_csv
1814
- return "#{@latitude} | #{@longitude} | #{@altitude}"
1906
+ return "#{@latitude}#{csv_delim}#{@longitude}#{csv_delim}#{@altitude}"
1815
1907
  end
1816
1908
 
1817
1909
  def latitude_as_float
@@ -1825,7 +1917,394 @@ HERE
1825
1917
  def altitude_as_float
1826
1918
  @altitude ? @altitude.to_f : invalid_altitude
1827
1919
  end
1920
+
1921
+ def degrees_diff(another_point)
1922
+ if (another_point)
1923
+ puts "this: #{to_s}" if false
1924
+ puts "other: #{another_point.to_s}" if false
1925
+ puts "lats: #{latitude_as_float} #{another_point.latitude_as_float}" if false
1926
+ puts "lngs: #{longitude_as_float} #{another_point.longitude_as_float}" if false
1927
+ lat_diff = latitude_as_float - another_point.latitude_as_float
1928
+ lng_diff = longitude_as_float - another_point.longitude_as_float
1929
+ diff = lat_diff.abs + lng_diff.abs
1930
+ puts "diff: #{diff} #{lat_diff} #{lng_diff}" if false
1931
+ diff
1932
+ else
1933
+ 360
1934
+ end
1935
+ end
1936
+
1937
+ def proximity(another_point, units)
1938
+ if (another_point)
1939
+ arg1 = latitude_as_float
1940
+ arg2 = another_point.latitude_as_float
1941
+ arg3 = latitude_as_float
1942
+ arg4 = another_point.latitude_as_float
1943
+ theta = longitude_as_float - another_point.longitude_as_float
1944
+ res1 = Math.sin(deg2rad(arg1))
1945
+ res2 = Math.sin(deg2rad(arg2))
1946
+ res3 = Math.cos(deg2rad(arg3))
1947
+ res4 = Math.cos(deg2rad(arg4))
1948
+ res5 = Math.cos(deg2rad(theta.to_f))
1949
+ dist = ((res1 * res2) + (res3 * res4 * res5)).to_f
1950
+
1951
+ if (!dist.nan?)
1952
+ dist = Math.acos(dist.to_f)
1953
+ if (!dist.nan?)
1954
+ dist = rad2deg(dist)
1955
+ if (!dist.nan?)
1956
+ dist = dist * 60 * 1.1515;
1957
+ if (!dist.nan?)
1958
+ if units == "K"
1959
+ dist = dist * 1.609344;
1960
+ end
1961
+ if units == "N"
1962
+ dist = dist * 0.8684;
1963
+ end
1964
+ end
1965
+ end
1966
+ end
1967
+ return dist.to_f
1968
+ else
1969
+ return 0
1970
+ end
1971
+ else
1972
+ return 0
1973
+ end
1974
+ end
1975
+
1976
+ def deg2rad(degrees)
1977
+ (((0 + degrees) * Math::PI) / 180)
1978
+ end
1979
+
1980
+ def rad2deg(radians)
1981
+ (((0 + radians) * 180) / Math::PI)
1982
+ end
1983
+ end
1984
+
1985
+ # =============================================================================
1986
+
1987
+ class CsvPoint < Point
1988
+
1989
+ attr_reader :rawdata, :tokens
1990
+ attr_reader :id, :run_id, :date, :time, :tkpt_num, :distance, :elapsed
1991
+ attr_reader :lap_number, :lap_distance, :lap_elapsed
1992
+ attr_accessor :course_distance, :course_elapsed, :degrees_diff
1993
+
1994
+ def initialize(csv_line)
1995
+ @rawdata = "#{csv_line}"
1996
+ @tokens = @rawdata.split(csv_delim)
1997
+ if (tokens.size > 12)
1998
+ @id = tokens[0] # <-- consists of @run_id.@tkpt_num for uniqueness and use as a DB table primary key.
1999
+ @run_id = tokens[1]
2000
+ @date = tokens[2]
2001
+ @time = tokens[3]
2002
+ @tkpt_num = tokens[4].strip.to_i
2003
+ @latitude = tokens[5].to_f
2004
+ @longitude = tokens[6].to_f
2005
+ @altitude = tokens[7].strip.to_f
2006
+ @distance = tokens[8].strip.to_f
2007
+ @elapsed = tokens[9]
2008
+ @lap_number = tokens[10].strip.to_i
2009
+ @lap_distance = tokens[11].strip.to_f
2010
+ @lap_elapsed = tokens[12]
2011
+ end
2012
+ end
2013
+
2014
+ def as_trackpoint
2015
+ tkpt = Trackpoint.new(@tkpt_num, @latitude, @longitude, @altitude, "#{date}T#{time}Z")
2016
+ tkpt.lap_number = @lap_number
2017
+ tkpt.lap_distance = @lap_distance
2018
+ tkpt.lap_elapsed = @lap_elapsed
2019
+ tkpt
2020
+ end
2021
+
2022
+ def to_s
2023
+ return "lat: #{@latitude} lng: #{@longitude} alt: #{@altitude} note: #{@note}"
2024
+ end
2025
+
2026
+ def to_formatted_string
2027
+ s = "lat: #{@latitude.to_s.ljust(20)}"
2028
+ s << " lng: #{@longitude.to_s.ljust(20)}"
2029
+ s << " time: #{@time}"
2030
+ pad = ''.ljust(70)
2031
+ s << "\n#{pad} course elapsed: #{@course_elapsed}"
2032
+ s << "\n#{pad} distance: #{@distance}"
2033
+ s << "\n#{pad} course distance: #{@course_distance}"
2034
+ s << "\n#{pad} altitude: #{@altitude}"
2035
+ s << "\n#{pad} degrees diff: #{@degrees_diff}"
2036
+ s
2037
+ end
2038
+ end
2039
+
2040
+ # =============================================================================
2041
+
2042
+ class CvsRun < GoobyObject
2043
+
2044
+ attr_reader :id, :points
2045
+
2046
+ def initialize(id)
2047
+ @id = "#{id}"
2048
+ @points = Array.new
2049
+ end
2050
+
2051
+ def add_point(point)
2052
+ if point
2053
+ @points << point
2054
+ end
2055
+ end
1828
2056
  end
2057
+
2058
+ # =============================================================================
2059
+
2060
+ class CsvReader < GoobyObject
2061
+
2062
+ attr_reader :files, :cvs_points
2063
+
2064
+ def initialize(array_of_filenames=nil)
2065
+ @files = Array.new
2066
+ @cvs_points = Array.new
2067
+ if array_of_filenames
2068
+ array_of_filenames.each { |filename| add_file(filename) }
2069
+ end
2070
+ end
2071
+
2072
+ def add_file(filename)
2073
+ if (filename)
2074
+ if (File.exist?(filename))
2075
+ @files << filename
2076
+ end
2077
+ end
2078
+ end
2079
+
2080
+ def read
2081
+ @files.each { |filename|
2082
+ lines = read_lines(filename, true)
2083
+ lines.each { |line|
2084
+ if (line.match('^#'))
2085
+ if (line.match('^#cols: '))
2086
+ col_names_header = line[7, line.size]
2087
+ @col_names = col_names_header.split('|')
2088
+ end
2089
+ else
2090
+ if (line.size > 50)
2091
+ @cvs_points << Gooby::CsvPoint.new(line)
2092
+ end
2093
+ end
2094
+ }
2095
+ }
2096
+ @cvs_points
2097
+ end
2098
+
2099
+ def display_formatted_record(record_index=2)
2100
+ tokens = @cvs_points[record_index].rawdata.split('|')
2101
+ puts "\nCsvReader.display_formatted_record hdr_cols=#{@col_names.size} data_cols=#{tokens.size}"
2102
+ size = 0
2103
+ @col_names.each { |col_name|
2104
+ size = size + 1
2105
+ if size <= tokens.size
2106
+ value = tokens[size - 1]
2107
+ puts "#{col_name.strip.ljust(20)} #{(size - 1).to_s.ljust(3)} #{value}"
2108
+ end
2109
+ }
2110
+ end
2111
+
2112
+ def to_s
2113
+ s = "CsvReader - file count: #{files.size} total points: #{cvs_points.size}"
2114
+ @files.each { |file| s << "\n file: #{file} "}
2115
+ s
2116
+ end
2117
+ end
2118
+
2119
+ # =============================================================================
2120
+
2121
+ class Trackpoint < Point
2122
+
2123
+ attr_accessor :first, :last, :number, :run_number, :dttm, :prev_tkpt, :lap_number, :lap_seq, :lap_distance, :lap_elapsed
2124
+ attr_accessor :cumulative_distance, :cumulative_pace, :incremental_distance, :split, :prev_split
2125
+ attr_accessor :first, :last, :run_id, :run_number
2126
+ attr_accessor :run_start_dttm
2127
+
2128
+ def initialize(num, lat, lng, alt, time_string, auxInfoHash=Hash.new(''))
2129
+ @number = num
2130
+ @run_number = 0
2131
+ @auxInfoHash = auxInfoHash
2132
+ @lap_seq = 1
2133
+ @lap_distance = 0
2134
+ lap_num = @auxInfoHash['lap_number']
2135
+ if (lap_num && lap_num.size > 0)
2136
+ @lap_number = lap_num.to_i
2137
+ else
2138
+ @lap_number = 0
2139
+ end
2140
+ # initialize superclass variables:
2141
+ @latitude = lat.to_s
2142
+ @longitude = lng.to_s
2143
+ feet = alt.to_f * 3.2736 # Convert from meters (in the Garmin xml) to feet.
2144
+ @altitude = feet.to_s
2145
+ @note = note.to_s
2146
+ @dttm = DtTm.new(time_string)
2147
+ @first = false
2148
+ @last = false
2149
+ @prev_tkpt = nil
2150
+ @cumulative_distance, @incremental_distance, @split = 0.0, 0.0, 0.0
2151
+ @cumulative_pace = ""
2152
+ end
2153
+
2154
+ public
2155
+
2156
+ def point
2157
+ Point.new(@latitude, @longitude, @altitude, @note)
2158
+ end
2159
+
2160
+ def position
2161
+ Point.new(@latitude, @longitude, @altitude, @note)
2162
+ end
2163
+
2164
+ def to_s
2165
+ "Tkpt: #{@number} #{super.to_s} date: #{@dttm.to_s} cdist: #{@cumulative_distance}"
2166
+ end
2167
+
2168
+ def to_csv(prev_tkpt=nil)
2169
+ first_lap_start_time_s = @auxInfoHash['first_lap_start_time']
2170
+ curr_lap_start_time_s = @auxInfoHash['curr_lap_start_time']
2171
+ lap_elapsed = ''
2172
+ total_elapsed = ''
2173
+
2174
+ if ((first_lap_start_time_s.size > 0) && (curr_lap_start_time_s.size > 0)) # garmin205 & 305
2175
+ first_lap_start_time = DtTm.new(first_lap_start_time_s)
2176
+ first_lap_start_time = @run_start_dttm
2177
+ curr_lap_start_time = DtTm.new(curr_lap_start_time_s)
2178
+ lap_elapsed = @dttm.hhmmss_diff(curr_lap_start_time)
2179
+ total_elapsed = @dttm.hhmmss_diff(first_lap_start_time)
2180
+ else # garmin 201
2181
+ total_elapsed = @dttm.hhmmss_diff(@run_start_dttm)
2182
+ lap_elapsed = total_elapsed
2183
+ end
2184
+
2185
+ delim = csv_delim
2186
+ csv = "#{@run_id}.#{@number}" # <-- primary key
2187
+ csv << "#{delim}#{@run_id}"
2188
+ csv << "#{delim}#{@dttm.yyyy_mm_dd_hh_mm_ss('|')}"
2189
+ csv << "#{delim}#{@number}"
2190
+ csv << "#{delim}#{position.to_csv}"
2191
+ csv << "#{delim}#{@cumulative_distance}"
2192
+ csv << "#{delim}#{total_elapsed}"
2193
+ csv << "#{delim}#{@lap_seq}"
2194
+ csv << "#{delim}#{@lap_distance}"
2195
+ csv << "#{delim}#{lap_elapsed}"
2196
+ csv
2197
+ end
2198
+
2199
+ def self.csv_header
2200
+ "#cols: primary_key|run_id|date|time|tkpt_num|latitude|longitude|altitude_ft|run_distance|run_elapsed|lap_tkpt_number|lap_distance|lap_elapsed"
2201
+ end
2202
+
2203
+ def to_geo_s
2204
+ ss = position.to_csv
2205
+ "Tkpt: #{@number} | #{ss} | #{@descr}"
2206
+ end
2207
+
2208
+ def compute_distance_and_pace(curr_index, start_dttm, prev_cumulative_dist, prev_trackpoint, units)
2209
+ @prev_tkpt = prev_trackpoint
2210
+ @cumulative_distance = prev_cumulative_dist.to_f
2211
+ @run_start_dttm = start_dttm
2212
+
2213
+ if @prev_tkpt
2214
+ @incremental_distance = proximity(@prev_tkpt, units)
2215
+ if (!@incremental_distance.nan?)
2216
+ @cumulative_distance = @cumulative_distance + @incremental_distance.to_f
2217
+ if (@lap_number == prev_trackpoint.lap_number)
2218
+ @lap_seq = prev_trackpoint.lap_seq + 1
2219
+ @lap_distance = prev_trackpoint.lap_distance + @incremental_distance
2220
+ else
2221
+ @lap_seq = 1
2222
+ @lap_distance = @incremental_distance
2223
+ end
2224
+ end
2225
+ compute_cumulative_pace(start_dttm)
2226
+ @cumulative_distance
2227
+ else
2228
+ @lap_seq = 1
2229
+ 0
2230
+ end
2231
+ end
2232
+
2233
+ def compute_cumulative_pace(start_dttm)
2234
+ if @cumulative_distance > 0
2235
+ secsDiff = @dttm.seconds_diff(start_dttm)
2236
+ secsMile = ((secsDiff.to_f) / (@cumulative_distance.to_f))
2237
+ minsMile = (secsMile / 60)
2238
+ wholeMins = minsMile.floor
2239
+ secsBal = secsMile - (wholeMins * 60)
2240
+ s1 = "#{secsDiff} #{secsMile} #{minsMile} #{wholeMins} #{secsBal} #{@cumulative_distance} | "
2241
+ s2 = sprintf("%d:%2.1f", minsMile, secsBal)
2242
+ @cumulative_pace = "#{s2}"
2243
+ else
2244
+ @cumulative_pace = ""
2245
+ end
2246
+ end
2247
+
2248
+ def set_split(n, tkpt)
2249
+ @split, @prev_split = n, tkpt
2250
+ end
2251
+
2252
+ def is_split()
2253
+ (@split >= 1)
2254
+ end
2255
+
2256
+ def split_info(dtTm)
2257
+ if is_split
2258
+ hhmmss = ''
2259
+ if @prev_split
2260
+ return "#{@split} #{@dttm.hhmmss_diff(@prev_split.dttm())}"
2261
+ else
2262
+ return "#{@split} #{@dttm.hhmmss_diff(dtTm)}"
2263
+ end
2264
+ else
2265
+ ""
2266
+ end
2267
+ end
2268
+
2269
+ public
2270
+
2271
+ def as_glatlng(comment_out, gen_comments, tkpt_count, curr_idx, start_dttm)
2272
+ comment_out ? comment = '// ' : comment = ''
2273
+ if gen_comments
2274
+ secs_diff = @dttm.seconds_diff(start_dttm)
2275
+ fmt_time = @dttm.hhmmss_diff(start_dttm)
2276
+ "\n #{comment}points.push(new GLatLng(#{latitude_as_float},#{longitude_as_float})); " +
2277
+ "// (#{curr_idx + 1} of #{tkpt_count}) #{@dttm.to_s} #{secs_diff} #{fmt_time} #{@cumulative_distance} #{split_info(start_dttm)} #{project_embedded_comment} "
2278
+ else
2279
+ "\n #{comment}points.push(new GLatLng(#{latitude_as_float},#{longitude_as_float})); // #{project_embedded_comment} "
2280
+ end
2281
+ end
2282
+
2283
+ def as_info_window_html(checkpoint, start_dttm)
2284
+ s = "\"<table align='left'>"
2285
+ if checkpoint
2286
+ secs_diff = @dttm.seconds_diff(start_dttm)
2287
+ fmt_time = @dttm.hhmmss_diff(start_dttm)
2288
+
2289
+ if checkpoint == 'Start'
2290
+ s << "<tr><td colspan='2'><b>Start!</b></td></tr>"
2291
+ elsif checkpoint == 'Finish'
2292
+ s << "<tr><td colspan='2'><b>Finish!</b></td></tr>"
2293
+ else
2294
+ s << "<tr><td colspan='2'><b>Checkpoint #{checkpoint}</b></td></tr>"
2295
+ end
2296
+ s << "<tr><td>Distance: </td><td>#{@cumulative_distance}</td></tr>"
2297
+ s << "<tr><td>Time of Day: </td><td>#{@dttm.to_s} </td></tr>"
2298
+ s << "<tr><td>Elapsed Time: </td><td>#{fmt_time} </td></tr>"
2299
+ s << "<tr><td>Average Pace: </td><td>#{@cumulative_pace} </td></tr>"
2300
+ s << "<tr><td>Lat/Lng: </td><td>#{latitude_as_float} , #{longitude_as_float} </td></tr>"
2301
+ #s << "<tr><td>Altitude: </td><td>#{altitude_as_float}m </td></tr>"
2302
+ s
2303
+ end
2304
+ s << "</table>\""
2305
+ s
2306
+ end
2307
+ end
1829
2308
 
1830
2309
  # =============================================================================
1831
2310
 
@@ -1839,19 +2318,20 @@ HERE
1839
2318
 
1840
2319
  class Run < GoobyObject
1841
2320
 
1842
- attr_accessor :number, :descr, :notes, :tracks, :tkpts, :laps, :distance
2321
+ attr_accessor :number, :run_id, :descr, :notes, :tracks, :tkpts, :laps, :distance
1843
2322
 
1844
2323
  def initialize(number=0, descr='')
1845
2324
  @number = number
2325
+ @run_id = nil
1846
2326
  @descr = descr
1847
2327
  @notes = ''
1848
2328
  @tracks = Array.new
1849
2329
  @tkpts = Array.new
1850
2330
  @laps = Array.new
1851
2331
  @distance = 0
1852
- @options = Hash.new
2332
+ @configuration = Hash.new
1853
2333
  @logProgress = true
1854
- @finished = false
2334
+ @finished = false
1855
2335
  end
1856
2336
 
1857
2337
  public
@@ -1860,7 +2340,7 @@ HERE
1860
2340
  def finish()
1861
2341
  @logProgress = false
1862
2342
  unless @finished
1863
- @tracks.each { |trk|
2343
+ @tracks.each { |trk|
1864
2344
  trk.trackpoints().each { |tkpt|
1865
2345
  tkpt.run_number = @number
1866
2346
  @tkpts.push(tkpt)
@@ -1868,6 +2348,7 @@ HERE
1868
2348
  }
1869
2349
  compute_distance_and_pace
1870
2350
  compute_splits
2351
+ set_run_ids
1871
2352
  @finished = true
1872
2353
  end
1873
2354
  end
@@ -1920,7 +2401,7 @@ HERE
1920
2401
  return last.hhmmss_diff(first)
1921
2402
  end
1922
2403
  end
1923
- return "??:??:??"
2404
+ return "00:00:00"
1924
2405
  end
1925
2406
 
1926
2407
  def start_yyyy_mm_dd
@@ -1965,12 +2446,14 @@ HERE
1965
2446
  puts "#{@number}|#{}|#{start_yyyy_mm_dd()}|#{start_hh_mm_ss()}|#{end_hh_mm_ss}|#{duration()}|#{@distance}|#{@tracks.size}|#{trackpoint_count()}|#{lap_count}|#{@notes.strip}"
1966
2447
  end
1967
2448
 
1968
- def put_tkpt_csv(with_header_comment=false)
2449
+ def put_tkpt_csv()
1969
2450
  finish() unless @finished
1970
- if with_header_comment
1971
- puts "# Run: #{@number} date: #{start_yyyy_mm_dd} dist: #{distance} dur: #{duration} trks: #{@tracks.size} tkpts: #{trackpoint_count} laps: #{lap_count} "
1972
- end
1973
- @tkpts.each { | tkpt | puts tkpt.to_csv }
2451
+ @tkpts.each { | tkpt |
2452
+ if (@prev_tkpt == nil)
2453
+ @prev_tkpt = tkpt
2454
+ end
2455
+ puts tkpt.to_csv(@prev_tkpt)
2456
+ }
1974
2457
  end
1975
2458
 
1976
2459
  def put_laps
@@ -1988,19 +2471,19 @@ HERE
1988
2471
  curr_index = curr_index + 1
1989
2472
  if curr_index == 0
1990
2473
  start_dttm = tkpt.dttm()
1991
- prev_tkpt = tkpt
2474
+ prev_tkpt = tkpt
1992
2475
  else
1993
2476
  cumulative_dist = tkpt.compute_distance_and_pace(curr_index, start_dttm, cumulative_dist, prev_tkpt, 'M')
1994
2477
  prev_tkpt = tkpt
1995
2478
  end
1996
2479
  }
1997
- @distance = cumulative_dist
2480
+ @distance = cumulative_dist
1998
2481
  end
1999
2482
 
2000
- def compute_splits
2483
+ def compute_splits
2001
2484
  nextSplitDist = 1.00
2002
2485
  prev_splitTkpt = nil
2003
- loop1Count = 0;
2486
+ loop1Count = 0;
2004
2487
  @tkpts.each { |tkpt|
2005
2488
  loop1Count = loop1Count + 1
2006
2489
  if tkpt.cumulative_distance() >= nextSplitDist
@@ -2017,6 +2500,15 @@ HERE
2017
2500
  tkpt.last = true if count == loop1Count
2018
2501
  }
2019
2502
  end
2503
+
2504
+ def set_run_ids
2505
+ @tkpts.each { |tkpt|
2506
+ if (@run_id == nil)
2507
+ @run_id = tkpt.dttm.rawdata
2508
+ end
2509
+ tkpt.run_id = @run_id
2510
+ }
2511
+ end
2020
2512
  end
2021
2513
 
2022
2514
  # =============================================================================
@@ -2092,201 +2584,117 @@ HERE
2092
2584
 
2093
2585
  def dump
2094
2586
  puts "Track: '#{@descr}' tkpts: #{size}"
2095
- @trackpoints.each { |tkpt| puts tkpt.to_csv } # to_geo_s
2587
+ @trackpoints.each { |tkpt| puts tkpt.to_csv }
2096
2588
  end
2097
2589
  end
2098
-
2099
- # =============================================================================
2100
-
2101
- =begin rdoc
2102
- Instances of this class represent a <Trackpoint> aggregate from a Forerunner
2103
- XML file. Additionally, there is distance, pace, and Google Map generation
2104
- logic in this class.
2105
-
2106
- <Trackpoint>
2107
- <Position>
2108
- <Latitude>35.49577</Latitude>
2109
- <Longitude>-80.83281</Longitude>
2110
- <Altitude>232.296</Altitude>
2111
- </Position>
2112
- <Time>2007-01-06T15:27:51Z</Time>
2113
- </Trackpoint>
2114
- =end
2115
-
2116
- class Trackpoint < Position
2117
-
2118
- attr_reader :first, :last, :number, :run_number, :dttm, :prev_tkpt, :descr
2119
- attr_reader :cumulative_distance, :cumulative_pace, :incremental_distance, :split, :prev_split
2120
- attr_writer :first, :last, :run_number
2121
2590
 
2122
- def initialize(num, lat, lng, alt, time_string, descr='')
2123
- @number = num
2124
- @run_number = 0
2125
-
2126
- # initialize superclass variables:
2127
- @latitude = lat.to_s
2128
- @longitude = lng.to_s
2129
- @altitude = alt.to_s
2130
- @note = note.to_s
2131
- @dttm = DtTm.new(time_string)
2132
- @first = false
2133
- @last = false
2134
- @prev_tkpt = nil
2135
- @cumulative_distance, @split = 0.0, 0.0
2136
- @cumulative_pace = ""
2137
- @descr = descr
2138
- end
2139
-
2140
- public
2141
-
2142
- def position
2143
- Position.new(@latitude, @longitude, @altitude, @note)
2144
- end
2145
-
2146
- def to_s
2147
- "Tkpt: #{@number} #{super.to_s} date: #{@dttm.to_s} cdist: #{@cumulative_distance}"
2148
- end
2149
-
2150
- def to_csv
2151
- ss = position.to_csv
2152
- "#{@run_number} | #{@dttm.to_s} | #{@number} | #{ss} | #{@cumulative_distance} "
2153
- end
2154
-
2155
- def to_geo_s
2156
- ss = position.to_csv
2157
- "Tkpt: #{@number} | #{ss} | #{@descr}"
2158
- end
2159
-
2160
- def compute_distance_and_pace(curr_index, start_dttm, prev_cumulative_dist, prev_trackpoint, units)
2161
- @prev_tkpt = prev_trackpoint
2162
- @cumulative_distance = prev_cumulative_dist.to_f
2163
-
2164
- if @prev_tkpt
2165
- arg1 = latitude().to_f
2166
- arg2 = @prev_tkpt.latitude().to_f
2167
- arg3 = latitude().to_f
2168
- arg4 = @prev_tkpt.latitude().to_f
2169
- theta = longitude().to_f - @prev_tkpt.longitude().to_f
2170
-
2171
- res1 = Math.sin(deg2rad(arg1))
2172
- res2 = Math.sin(deg2rad(arg2))
2173
- res3 = Math.cos(deg2rad(arg3))
2174
- res4 = Math.cos(deg2rad(arg4))
2175
- res5 = Math.cos(deg2rad(theta.to_f))
2176
-
2177
- incremental_distance = ((res1 * res2) + (res3 * res4 * res5)).to_f
2178
-
2179
- if (!incremental_distance.nan?)
2180
- incremental_distance = Math.acos(incremental_distance.to_f)
2181
- if (!incremental_distance.nan?)
2182
- incremental_distance = rad2deg(incremental_distance)
2183
- if (!incremental_distance.nan?)
2184
- incremental_distance = incremental_distance * 60 * 1.1515;
2185
- if (!incremental_distance.nan?)
2186
- if units == "K"
2187
- incremental_distance = incremental_distance * 1.609344;
2188
- end
2189
- if units == "N"
2190
- incremental_distance = incremental_distance * 0.8684;
2191
- end
2192
- @cumulative_distance = @cumulative_distance + incremental_distance.to_f
2591
+ # =============================================================================
2592
+
2593
+ class Course < GoobyObject
2594
+
2595
+ attr_accessor :name, :distance, :point_numbers, :points
2596
+
2597
+ def initialize(yaml_csv)
2598
+ @name, @distance = '', 0.0
2599
+ @point_numbers, @points, @bad_points = Array.new, Array.new, Array.new
2600
+ @points_hash, @matched_points = Hash.new, Hash.new
2601
+ tokens = yaml_csv.split(',')
2602
+ @name = tokens[0] if tokens.size > 0
2603
+ @distance = tokens[1].to_f if tokens.size > 1
2604
+ if tokens.size > 2
2605
+ index = 0
2606
+ tokens.each { |tok|
2607
+ index = index + 1
2608
+ if (index > 2)
2609
+ poi = Configuration.get_config.get_poi(tok)
2610
+ if (poi)
2611
+ poi.number = "#{tok}"
2612
+ @point_numbers << "#{tok}"
2613
+ @points << poi
2614
+ @points_hash["#{tok}"] = poi
2615
+ else
2616
+ @bad_points << tok
2193
2617
  end
2194
2618
  end
2195
- end
2196
- end
2197
- compute_cumulative_pace(start_dttm)
2198
- @cumulative_distance
2199
- else
2200
- 0
2201
- end
2202
- end
2203
-
2204
- def compute_cumulative_pace(start_dttm)
2205
- if @cumulative_distance > 0
2206
- secsDiff = @dttm.seconds_diff(start_dttm)
2207
- secsMile = ((secsDiff.to_f) / (@cumulative_distance.to_f))
2208
- minsMile = (secsMile / 60)
2209
- wholeMins = minsMile.floor
2210
- secsBal = secsMile - (wholeMins * 60)
2211
- s1 = "#{secsDiff} #{secsMile} #{minsMile} #{wholeMins} #{secsBal} #{@cumulative_distance} | "
2212
- s2 = sprintf("%d:%2.1f", minsMile, secsBal)
2213
- @cumulative_pace = "#{s2}"
2214
- else
2215
- @cumulative_pace = ""
2619
+ }
2620
+ end
2216
2621
  end
2217
- end
2218
-
2219
- def set_split(n, tkpt)
2220
- @split, @prev_split = n, tkpt
2221
- end
2222
-
2223
- def is_split()
2224
- (@split >= 1)
2225
- end
2226
-
2227
- def split_info(dtTm)
2228
- if is_split
2229
- hhmmss = ''
2230
- if @prev_split
2231
- return "#{@split} #{@dttm.hhmmss_diff(@prev_split.dttm())}"
2232
- else
2233
- return "#{@split} #{@dttm.hhmmss_diff(dtTm)}"
2234
- end
2235
- else
2236
- ""
2622
+
2623
+ def has_errors
2624
+ (@bad_points.size > 0) ? true : false
2625
+ end
2626
+
2627
+ def matched(number, point)
2628
+ @matched_points["#{number}"] = point if point
2629
+ end
2630
+
2631
+ def matched?
2632
+ (@matched_points.size == @point_numbers.size) ? true : false
2633
+ end
2634
+
2635
+ def display_matches
2636
+ puts ''
2637
+ calculate_matches
2638
+ @point_numbers.each { |num|
2639
+ point = @points_hash["#{num}"]
2640
+ mpoint = @matched_points["#{num}"]
2641
+ puts ''
2642
+ puts " Course Point: #{point.to_formatted_string}" if point
2643
+ puts " Matched Point: #{mpoint.to_formatted_string}" if mpoint
2644
+ }
2645
+ puts ''
2237
2646
  end
2238
- end
2239
-
2240
- private
2241
-
2242
- def deg2rad(degrees)
2243
- (((0 + degrees) * Math::PI) / 180)
2244
- end
2245
-
2246
- def rad2deg(radians)
2247
- (((0 + radians) * 180) / Math::PI)
2248
- end
2249
2647
 
2250
- public
2251
-
2252
- def as_glatlng(comment_out, gen_comments, tkpt_count, curr_idx, start_dttm)
2253
- comment_out ? comment = '// ' : comment = ''
2254
- if gen_comments
2255
- secs_diff = @dttm.seconds_diff(start_dttm)
2256
- fmt_time = @dttm.hhmmss_diff(start_dttm)
2257
- "\n #{comment}points.push(new GLatLng(#{latitude_as_float},#{longitude_as_float})); " +
2258
- "// (#{curr_idx + 1} of #{tkpt_count}) #{@dttm.to_s} #{secs_diff} #{fmt_time} #{@cumulative_distance} #{split_info(start_dttm)} #{project_embedded_comment} "
2259
- else
2260
- "\n #{comment}points.push(new GLatLng(#{latitude_as_float},#{longitude_as_float})); // #{project_embedded_comment} "
2261
- end
2262
- end
2263
-
2264
- def as_info_window_html(checkpoint, start_dttm)
2265
- s = "\"<table align='left'>"
2266
- if checkpoint
2267
- secs_diff = @dttm.seconds_diff(start_dttm)
2268
- fmt_time = @dttm.hhmmss_diff(start_dttm)
2269
-
2270
- if checkpoint == 'Start'
2271
- s << "<tr><td colspan='2'><b>Start!</b></td></tr>"
2272
- elsif checkpoint == 'Finish'
2273
- s << "<tr><td colspan='2'><b>Finish!</b></td></tr>"
2274
- else
2275
- s << "<tr><td colspan='2'><b>Checkpoint #{checkpoint}</b></td></tr>"
2276
- end
2277
- s << "<tr><td>Distance: </td><td>#{@cumulative_distance}</td></tr>"
2278
- s << "<tr><td>Time of Day: </td><td>#{@dttm.to_s} </td></tr>"
2279
- s << "<tr><td>Elapsed Time: </td><td>#{fmt_time} </td></tr>"
2280
- s << "<tr><td>Average Pace: </td><td>#{@cumulative_pace} </td></tr>"
2281
- s << "<tr><td>Lat/Lng: </td><td>#{latitude_as_float} , #{longitude_as_float} </td></tr>"
2282
- s << "<tr><td>Altitude: </td><td>#{altitude_as_float}m </td></tr>"
2283
- s
2648
+ def reset
2649
+ @matched_points = Hash.new
2284
2650
  end
2285
- s << "</table>\""
2286
- s
2651
+
2652
+ def to_s
2653
+ "#{@name} #{@distance} points: #{@points.size} errors: #{has_errors}"
2654
+ end
2655
+
2656
+ def dump
2657
+ puts "Course: #{@name}"
2658
+ puts "Distance: #{@distance}"
2659
+ points.each { |pt| puts pt } #{@points.size} errors: #{has_errors}"
2660
+ end
2661
+
2662
+ private
2663
+
2664
+ def calculate_matches
2665
+ # first, identify the high and low distance points and their indices.
2666
+ idx, low_dist, low_dist_idx, low_time, high_dist, high_dist_idx = -1, 999999.0, 0, '', -1.0, 0
2667
+ @point_numbers.each { |num|
2668
+ idx = idx + 1
2669
+ mpoint = @matched_points["#{num}"]
2670
+ if mpoint && mpoint.distance < low_dist
2671
+ low_dist = mpoint.distance
2672
+ low_dist_idx = idx
2673
+ low_time = mpoint.elapsed
2674
+ end
2675
+ if mpoint && mpoint.distance > high_dist
2676
+ high_dist = mpoint.distance
2677
+ high_dist_idx = idx
2678
+ end
2679
+ }
2680
+ low_dttm = DtTm.new("2007-06-09T#{low_time}Z")
2681
+
2682
+ # reorder the entries in @point_numbers if necessary - 'low-to-high distance'.
2683
+ if (high_dist_idx < low_dist_idx)
2684
+ @point_numbers.reverse!
2685
+ end
2686
+
2687
+ @point_numbers.each { |num|
2688
+ mpoint = @matched_points["#{num}"]
2689
+ if mpoint
2690
+ mpoint.course_distance = mpoint.distance - low_dist
2691
+ dttm = DtTm.new("2007-06-09T#{mpoint.elapsed}Z")
2692
+ mpoint.course_elapsed = dttm.hhmmss_diff(low_dttm)
2693
+ end
2694
+ }
2695
+ end
2287
2696
  end
2288
- end
2289
-
2697
+
2290
2698
  # =============================================================================
2291
2699
 
2292
2700
  =begin rdoc
@@ -2361,7 +2769,7 @@ HERE
2361
2769
  type = tokens[0].ljust(6)
2362
2770
  name = tokens[1]
2363
2771
  count = tokens[2].rjust(6)
2364
- puts " #{count} #{type} #{name}"
2772
+ puts " #{count} #{type} #{name}"
2365
2773
  }
2366
2774
  end
2367
2775
 
@@ -2409,8 +2817,6 @@ HERE
2409
2817
  count = @tokens_hash.value(name)
2410
2818
  puts "method: #{name} (#{count})"
2411
2819
  }
2412
- # puts @tokens_hash.to_s
2413
- # puts @tokens_hash.size
2414
2820
  end
2415
2821
 
2416
2822
  def parse_code_lines
@@ -2596,13 +3002,40 @@ HERE
2596
3002
 
2597
3003
  class GoobyCommand < GoobyObject
2598
3004
 
2599
- def initialize
3005
+ attr_reader :configuration
3006
+
3007
+ def initialize(gooby_yaml_filename=nil)
3008
+ @configuration = Gooby::Configuration.init(gooby_yaml_filename)
2600
3009
  end
2601
3010
 
2602
- def options(yaml_filename)
2603
- Gooby::Options.new(yaml_filename)
3011
+ def display_version
3012
+ s = "# #{project_name} #{project_version_number} #{project_date}. #{project_copyright}."
3013
+ puts s
2604
3014
  end
2605
3015
 
3016
+ def split_garmin_export_file(argv)
3017
+ if (argv == nil)
3018
+ puts "ERROR: no ARGV args passed."
3019
+ elsif (argv.size < 3)
3020
+ puts ""
3021
+ puts "Invalid program args; three args required - format, input filename, output directory"
3022
+ puts " the first arg, format, should be one of: garmin201, garmin205, garmin305, etc."
3023
+ puts " the second arg is the input filename - which is a garmin export file"
3024
+ puts " the third arg is the output directory where the split files are written to\n\n"
3025
+ puts "Please correct the program arguments and try again. \n\n"
3026
+ else
3027
+ format = argv[0].downcase
3028
+ filename = argv[1]
3029
+ out_dir = argv[2]
3030
+
3031
+ if (format == 'garmin201')
3032
+ split_garmin_forerunner_logbook_xml(filename, out_dir)
3033
+ else
3034
+ split_garmin_training_center_xml(filename, out_dir)
3035
+ end
3036
+ end
3037
+ end
3038
+
2606
3039
  def split_garmin_forerunner_logbook_xml(xml_filename, out_dir)
2607
3040
  splitter = Gooby::ForerunnerXmlSplitter.new(xml_filename, out_dir)
2608
3041
  splitter.split
@@ -2612,22 +3045,117 @@ HERE
2612
3045
  splitter = Gooby::TrainingCenterXmlSplitter.new(tcx_filename, out_dir)
2613
3046
  splitter.split
2614
3047
  end
3048
+
3049
+ def parse_garmin_xml_file(argv)
3050
+ if (argv == nil)
3051
+ puts "ERROR: no ARGV args passed."
3052
+ elsif (argv.size < 2)
3053
+ puts ""
3054
+ puts "Invalid program args; two args required - format, input (xml) filename, output directory"
3055
+ puts " the first arg, format, should be one of: garmin201, garmin205, garmin305, etc."
3056
+ puts " the second arg, input xml filename, was produced by the Gooby 'splitter.rb' program."
3057
+ puts "Please correct the program arguments and try again. \n\n"
3058
+ else
3059
+ format = argv[0].downcase
3060
+ filename = argv[1]
3061
+ puts Trackpoint.csv_header
3062
+ if (format == 'garmin201')
3063
+ parse_garmin_forerunner_logbook_xml(filename)
3064
+ else
3065
+ parse_garmin_training_center_xml(filename)
3066
+ end
3067
+ end
3068
+ end
2615
3069
 
2616
3070
  def parse_garmin_forerunner_logbook_xml(xml_filename)
2617
3071
  handler = Gooby::ForerunnerXmlParser.new
2618
3072
  Document.parse_stream((File.new xml_filename), handler)
2619
- handler.put_all_run_tkpt_csv(true)
3073
+ handler.put_all_run_tkpt_csv()
2620
3074
  end
2621
3075
 
2622
3076
  def parse_garmin_training_center_xml(tcx_filename)
2623
3077
  handler = Gooby::TrainingCenterXmlParser.new
2624
3078
  Document.parse_stream((File.new tcx_filename), handler)
2625
- handler.put_all_run_tkpt_csv(true)
3079
+ handler.put_all_run_tkpt_csv()
3080
+ end
3081
+
3082
+ def read_csv_files(array_of_filenames, record_index=0)
3083
+ @cvs_reader = Gooby::CsvReader.new(array_of_filenames)
3084
+ @cvs_points = @cvs_reader.read
3085
+ puts @cvs_reader.to_s
3086
+ if (record_index > 0)
3087
+ @cvs_reader.display_formatted_record(record_index)
3088
+ end
3089
+ @cvs_points
2626
3090
  end
2627
3091
 
2628
- def generate_google_map(csv_filename, options_obj)
2629
- generator = Gooby::GoogleMapGenerator.new(csv_filename)
2630
- generator.generate_page(options_obj)
3092
+ def been_there(course_id, proximity=0.0070, uom='deg')
3093
+ unless @cvs_points
3094
+ puts "You must first invoke method 'read_csv_files' with a list of parsed CSV filenames."
3095
+ return
3096
+ end
3097
+ course = configuration.get_course("#{course_id}")
3098
+ unless course
3099
+ puts "Unable to find course id '#{course_id}' in the gooby config yaml file."
3100
+ return
3101
+ end
3102
+ puts ''
3103
+ # collect the cvs_points into run arrays
3104
+ @curr_run_id = 'x'
3105
+ @cvs_runs = Array.new
3106
+ @cvs_points.each { |cvs_point|
3107
+ if (cvs_point.run_id == @curr_run_id)
3108
+ @curr_run.add_point(cvs_point)
3109
+ else
3110
+ @curr_run_id = cvs_point.run_id
3111
+ @curr_run = Gooby::CvsRun.new(@curr_run_id)
3112
+ @curr_run.add_point(cvs_point)
3113
+ @cvs_runs << @curr_run
3114
+ end
3115
+ }
3116
+
3117
+ # iterate the runs - looking for a match vs the course
3118
+ @cvs_runs.each { |cvs_run|
3119
+ puts "Scanning run id '#{cvs_run.id}', point count= #{cvs_run.points.size}" if false
3120
+ run_points = cvs_run.points
3121
+ course.reset
3122
+ course.points.each { |course_point|
3123
+ closest_diff = 999
3124
+ closest_point = nil
3125
+ run_points.each { |run_point|
3126
+ diff = course_point.degrees_diff(run_point)
3127
+ if ((diff < proximity) && (diff < closest_diff))
3128
+ closest_diff = diff
3129
+ closest_point = run_point
3130
+ closest_point.degrees_diff = diff
3131
+ course.matched(course_point.number, run_point)
3132
+ end
3133
+ }
3134
+ }
3135
+ if course.matched?
3136
+ puts "Course '#{course.name}' matched vs run id '#{cvs_run.id}'"
3137
+ course.display_matches
3138
+ else
3139
+ puts "course not matched" if false
3140
+ end
3141
+ }
3142
+ end
3143
+
3144
+ def generate_google_map(argv)
3145
+ if (argv == nil)
3146
+ puts "ERROR: no ARGV args passed."
3147
+ elsif (argv.size < 1)
3148
+ puts ""
3149
+ puts "Invalid program args; one required - input csv filename"
3150
+ puts " the first arg, csv filename, was produced by the Gooby 'parser.rb' program."
3151
+ puts "Please correct the program arguments and try again. \n\n"
3152
+ else
3153
+ csv_filename = argv[0]
3154
+ configuration = Gooby::Configuration.get_config
3155
+ generator = Gooby::GoogleMapGenerator.new(csv_filename)
3156
+ generator.generate_page(configuration)
3157
+ end
3158
+
2631
3159
  end
2632
3160
  end
2633
3161
  end # end of module