gooby 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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