Class: WhereWasI::Gpx
- Inherits:
-
Object
- Object
- WhereWasI::Gpx
- Defined in:
- lib/where_was_i/gpx.rb
Overview
Use a GPX file as a data source for location inferences.
Constant Summary collapse
- Infinity =
1.0/0
Instance Attribute Summary collapse
-
#intersegment_behavior ⇒ Object
readonly
Returns the value of attribute intersegment_behavior.
-
#tracks ⇒ Object
readonly
Returns the value of attribute tracks.
Instance Method Summary collapse
-
#add_tracks ⇒ Object
extract track data from gpx data.
-
#at(time) ⇒ Hash
infer a location from track data and a time.
-
#initialize(gpx_file: nil, gpx_data: nil, intersegment_behavior: nil) ⇒ Gpx
constructor
A new instance of Gpx.
Constructor Details
#initialize(gpx_file: nil, gpx_data: nil, intersegment_behavior: nil) ⇒ Gpx
Returns a new instance of Gpx.
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/where_was_i/gpx.rb', line 21 def initialize(gpx_file:nil, gpx_data:nil, intersegment_behavior:nil) if gpx_file @gpx_data = open(gpx_file) elsif gpx_data @gpx_data = gpx_data else raise ArgumentError, "Must supply gpx_file or gpx_data." end valid_values = [nil, :interpolate, :nearest] if !valid_values.include?(intersegment_behavior) raise ArgumentError, "intersegment_behavior must be one of: #{valid_values.inspect}" end @intersegment_behavior = intersegment_behavior @tracks_added = false end |
Instance Attribute Details
#intersegment_behavior ⇒ Object (readonly)
Returns the value of attribute intersegment_behavior.
7 8 9 |
# File 'lib/where_was_i/gpx.rb', line 7 def intersegment_behavior @intersegment_behavior end |
#tracks ⇒ Object (readonly)
Returns the value of attribute tracks.
7 8 9 |
# File 'lib/where_was_i/gpx.rb', line 7 def tracks @tracks end |
Instance Method Details
#add_tracks ⇒ Object
extract track data from gpx data
it’s not necessary to call this directly
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/where_was_i/gpx.rb', line 42 def add_tracks @tracks = [] doc = Nokogiri::XML(@gpx_data) doc.css('xmlns|trk').each do |trk| track = Track.new trk.css('xmlns|trkpt').each do |trkpt| # https://en.wikipedia.org/wiki/GPS_Exchange_Format#Units # decimal degrees, wgs84. # elevation in meters. track.add_point( lat: trkpt.attributes['lat'].text.to_f, lon: trkpt.attributes['lon'].text.to_f, elevation: trkpt.at_css('xmlns|ele').text.to_f, time: Time.parse(trkpt.at_css('xmlns|time').text) ) end @tracks << track end @intersegments = [] @tracks.each_with_index do |track,i| next if i == 0 this_track = track prev_track = @tracks[i-1] inter_track = Track.new inter_track.add_point( lat: prev_track.end_location[0], lon: prev_track.end_location[1], elevation: prev_track.end_location[2], time: prev_track.end_time ) inter_track.add_point( lat: this_track.start_location[0], lon: this_track.start_location[1], elevation: this_track.start_location[2], time: this_track.start_time ) @intersegments << inter_track end @tracks_added = true end |
#at(time) ⇒ Hash
infer a location from track data and a time
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/where_was_i/gpx.rb', line 93 def at(time) add_tracks if ! @tracks_added if time.is_a?(String) time = Time.parse(time) end time = time.to_i location = nil @tracks.each do |track| location = track.at(time) break if location end if ! location case @intersegment_behavior when :interpolate then @intersegments.each do |track| location = track.at(time) break if location end when :nearest then # hash is sorted in ascending time order. # all start/end points for all segments points = {} @tracks.each do |t| points[t.start_time.to_i] = t.start_location points[t.end_time.to_i] = t.end_location end last_diff = Infinity last_time = -1 points.each do |p_time,p_location| this_diff = (p_time.to_i - time).abs # as long as the differences keep getting smaller, we keep going # as soon as we see a larger one, we step back one and use that value. if this_diff > last_diff l = points[last_time] location = Track.array_to_hash(points[last_time]) break else last_diff = this_diff last_time = p_time end end # if we got here, time is > the end of the last segment location = Track.array_to_hash(points[last_time]) end end # each segment has a begin and end time. # which one is this time closest to? # array of times in order. compute abs diff between time and each point. # put times in order. abs diff to each, until we get a larger value or we run out of points. then back up one and use that. # {time => [lat, lon, elev], time => [lat, lon, elev]} location end |