Class: CycleAnalystLogger::Gpx

Inherits:
Object
  • Object
show all
Defined in:
lib/cycle_analyst_logger/gpx.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filename) ⇒ Gpx

Returns a new instance of Gpx.



17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/cycle_analyst_logger/gpx.rb', line 17

def initialize(filename)
  @gpx = GPX::GPXFile.new(gpx_file: filename)
  @points = gpx.tracks.flat_map(&:points).each_with_object({}) do |point, memo|
    memo[point.time] = {
      timestamp: point.time,
      lattitude: point.lat,
      longitude: point.lon,
      elevation: point.elevation,
      speed: point.speed
    }
  end
end

Instance Attribute Details

#gpxObject (readonly)

Returns the value of attribute gpx.



6
7
8
# File 'lib/cycle_analyst_logger/gpx.rb', line 6

def gpx
  @gpx
end

#pointsHash<Time, Hash> (readonly)

The clean info needed from the gpx

  • :time (Hash<Symbol, Float) The Point Atrributes

    • :timestamp (Time) in seconds resolution

    • :lattitude (Float)

    • :longitude (Float)

    • :elevation (Float)

    • :speed (Float)

Returns:

  • (Hash<Time, Hash>)

    points The points organized by Timestamp.



15
16
17
# File 'lib/cycle_analyst_logger/gpx.rb', line 15

def points
  @points
end

Instance Method Details

#closest_point_in_time(timestamp) ⇒ Object

Finds the point that is close to the timestamp input.

If there is no point with the same time, it returns the last point
Also calculates the speed between the last point and this point and adds it to the response hash

Parameters:

  • timestamp (Time)

    Timestamp to find. Will round to nearest second



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/cycle_analyst_logger/gpx.rb', line 45

def closest_point_in_time(timestamp)
  point = if (point = points[timestamp.round(0)])
            # Handle initial case
            @last_point = point unless @last_point

            if point[:timestamp] == @last_point[:timestamp]
              @last_point 
            else # Its a new point so calculate speed and return the new point
              duration = point[:timestamp] - @last_point[:timestamp]
              distance = Haversine.distance(@last_point[:lattitude], @last_point[:longitude], point[:lattitude], point[:longitude]).to_miles
              speed_mph = (distance / duration) * 3600 # Convert miles / second to miles per hour
              @last_point = point.merge(speed: speed_mph)
            end

          else # Could not find a point with a matching timestamp so use the last_point (were probably not moving or Strava not recording)
            @last_point.merge(speed: 0.0)
          end
end

#merge_location(log_filename, output_filename = nil) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/cycle_analyst_logger/gpx.rb', line 64

def merge_location(log_filename, output_filename = nil)
  # Get the proper output File descriptor
  out_fd = if output_filename
             File.open(output_filename, 'w')
           else
             $stdout.dup
           end

  File.readlines(log_filename).each.with_index do |log_line, idx|
    log_record = log_line.split(',')
    if idx == 0
      out_fd.puts 'Timestamp,Lattitude,Longitude,Elevation (feet),Speed (mph),' +
                  log_record[1..-1].join(',')
      next
    end

    # Check that the line has a valid timestamp, skip this line if it isn't
    next unless (timestamp = valid_timestamp log_record[0])

    # Get the point from GPX that is closest to the timestamp from the log
    if (point = closest_point_in_time(timestamp))
      lattitude = point[:lattitude]
      longitude = point[:longitude]
      elevation = point[:elevation]
      speed = point[:speed]
    else
      lattitude = longitude = elevation = speed = nil
    end
    out_fd.puts "#{log_record[0]},#{lattitude},#{longitude},#{elevation},#{speed}," +
                log_record[1..-1].join(',')
  end
end

#valid_timestamp(input) ⇒ Time?

Checks if the input is a valid Time String

Parameters:

  • input (String)

    Potential Time Stamp or invalid string

Returns:

  • (Time, nil)

    Returns a Time object based on the input if it was valid. Otherwise returns nil



33
34
35
36
37
38
39
# File 'lib/cycle_analyst_logger/gpx.rb', line 33

def valid_timestamp(input)
  begin
    Time.parse(input)
  rescue ArgumentError
    nil
  end
end