gpx 0.8.3 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +37 -0
  3. data/.gitignore +4 -0
  4. data/.rubocop +1 -0
  5. data/.rubocop.yml +162 -0
  6. data/.ruby-version +1 -0
  7. data/.tool-versions +1 -0
  8. data/.travis.yml +4 -6
  9. data/CHANGELOG.md +32 -0
  10. data/Gemfile +2 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +38 -17
  13. data/Rakefile +22 -12
  14. data/UPGRADING.md +7 -0
  15. data/bin/gpx_distance +5 -6
  16. data/bin/gpx_smooth +25 -26
  17. data/gpx.gemspec +14 -11
  18. data/lib/gpx/bounds.rb +13 -31
  19. data/lib/gpx/geo_json.rb +199 -0
  20. data/lib/gpx/gpx.rb +4 -26
  21. data/lib/gpx/gpx_file.rb +140 -134
  22. data/lib/gpx/magellan_track_log.rb +34 -66
  23. data/lib/gpx/point.rb +22 -35
  24. data/lib/gpx/route.rb +10 -31
  25. data/lib/gpx/segment.rb +63 -90
  26. data/lib/gpx/track.rb +38 -42
  27. data/lib/gpx/track_point.rb +32 -0
  28. data/lib/gpx/version.rb +3 -1
  29. data/lib/gpx/waypoint.rb +10 -34
  30. data/lib/gpx.rb +13 -34
  31. data/tests/geojson_files/combined_data.json +68 -0
  32. data/tests/geojson_files/line_string_data.json +83 -0
  33. data/tests/geojson_files/multi_line_string_data.json +74 -0
  34. data/tests/geojson_files/multi_point_data.json +14 -0
  35. data/tests/geojson_files/point_data.json +22 -0
  36. data/tests/geojson_test.rb +92 -0
  37. data/tests/gpx10_test.rb +7 -6
  38. data/tests/gpx_file_test.rb +31 -19
  39. data/tests/gpx_files/one_segment_mixed_times.gpx +884 -0
  40. data/tests/gpx_files/routes_without_names.gpx +29 -0
  41. data/tests/gpx_files/with_empty_tracks.gpx +72 -0
  42. data/tests/magellan_test.rb +12 -11
  43. data/tests/output_test.rb +93 -94
  44. data/tests/route_test.rb +75 -30
  45. data/tests/segment_test.rb +104 -93
  46. data/tests/track_file_test.rb +50 -70
  47. data/tests/track_point_test.rb +22 -11
  48. data/tests/track_test.rb +73 -61
  49. data/tests/waypoint_test.rb +46 -48
  50. metadata +45 -13
  51. data/lib/gpx/trackpoint.rb +0 -60
data/lib/gpx/route.rb CHANGED
@@ -1,58 +1,37 @@
1
- #--
2
- # Copyright (c) 2006 Doug Fales
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #++
1
+ # frozen_string_literal: true
2
+
23
3
  module GPX
24
4
  # A Route in GPX is very similar to a Track, but it is created by a user
25
5
  # from a series of Waypoints, whereas a Track is created by the GPS device
26
6
  # automatically logging your progress at regular intervals.
27
7
  class Route < Base
28
-
29
8
  attr_accessor :points, :name, :gpx_file
30
9
 
31
10
  # Initialize a Route from a XML::Node.
32
11
  def initialize(opts = {})
33
- if(opts[:gpx_file] and opts[:element])
12
+ super()
13
+ if opts[:gpx_file] && opts[:element]
34
14
  rte_element = opts[:element]
35
15
  @gpx_file = opts[:gpx_file]
36
- @name = rte_element.at("name").inner_text
16
+ @name = rte_element.at('name')&.inner_text
37
17
  @points = []
38
- rte_element.search("rtept").each do |point|
39
- @points << Point.new(:element => point, :gpx_file => @gpx_file)
18
+ rte_element.search('rtept').each do |point|
19
+ @points << Point.new(element: point, gpx_file: @gpx_file)
40
20
  end
41
21
  else
42
- @points = (opts[:points] or [])
22
+ @points = (opts[:points] || [])
43
23
  @name = (opts[:name])
44
24
  end
45
-
46
25
  end
47
26
 
48
27
  # Delete points outside of a given area.
49
28
  def crop(area)
50
- points.delete_if{ |pt| not area.contains? pt }
29
+ points.delete_if { |pt| !area.contains? pt }
51
30
  end
52
31
 
53
32
  # Delete points within the given area.
54
33
  def delete_area(area)
55
- points.delete_if{ |pt| area.contains? pt }
34
+ points.delete_if { |pt| area.contains? pt }
56
35
  end
57
36
  end
58
37
  end
data/lib/gpx/segment.rb CHANGED
@@ -1,38 +1,18 @@
1
- #--
2
- # Copyright (c) 2006 Doug Fales
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #++
1
+ # frozen_string_literal: true
2
+
23
3
  module GPX
24
4
  # A segment is the basic container in a GPX file. A Segment contains points
25
5
  # (in this lib, they're called TrackPoints). A Track contains Segments. An
26
6
  # instance of Segment knows its highest point, lowest point, earliest and
27
7
  # latest points, distance, and bounds.
28
8
  class Segment < Base
29
-
30
9
  attr_reader :earliest_point, :latest_point, :bounds, :highest_point, :lowest_point, :distance, :duration
31
10
  attr_accessor :points, :track
32
11
 
33
12
  # If a XML::Node object is passed-in, this will initialize a new
34
13
  # Segment based on its contents. Otherwise, a blank Segment is created.
35
14
  def initialize(opts = {})
15
+ super()
36
16
  @gpx_file = opts[:gpx_file]
37
17
  @track = opts[:track]
38
18
  @points = []
@@ -43,15 +23,13 @@ module GPX
43
23
  @distance = 0.0
44
24
  @duration = 0.0
45
25
  @bounds = Bounds.new
46
- if(opts[:element])
47
- segment_element = opts[:element]
48
- last_pt = nil
49
- if segment_element.is_a?(Nokogiri::XML::Node)
50
- segment_element.search("trkpt").each do |trkpt|
51
- pt = TrackPoint.new(:element => trkpt, :segment => self, :gpx_file => @gpx_file)
52
- append_point(pt)
53
- end
54
- end
26
+
27
+ segment_element = opts[:element]
28
+ return unless segment_element.is_a?(Nokogiri::XML::Node)
29
+
30
+ segment_element.search('trkpt').each do |trkpt|
31
+ pt = TrackPoint.new(element: trkpt, segment: self, gpx_file: @gpx_file)
32
+ append_point(pt)
55
33
  end
56
34
  end
57
35
 
@@ -59,8 +37,8 @@ module GPX
59
37
  def append_point(pt)
60
38
  last_pt = @points[-1]
61
39
  if pt.time
62
- @earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time)
63
- @latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time)
40
+ @earliest_point = pt if @earliest_point.nil? || (@earliest_point.time && pt.time < @earliest_point.time)
41
+ @latest_point = pt if @latest_point.nil? || (@latest_point.time && pt.time > @latest_point.time)
64
42
  else
65
43
  # when no time information in data, we consider the points are ordered
66
44
  @earliest_point = @points[0]
@@ -68,8 +46,8 @@ module GPX
68
46
  end
69
47
 
70
48
  if pt.elevation
71
- @lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
72
- @highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
49
+ @lowest_point = pt if @lowest_point.nil? || (pt.elevation < @lowest_point.elevation)
50
+ @highest_point = pt if @highest_point.nil? || (pt.elevation > @highest_point.elevation)
73
51
  end
74
52
  @bounds.min_lat = pt.lat if pt.lat < @bounds.min_lat
75
53
  @bounds.min_lon = pt.lon if pt.lon < @bounds.min_lon
@@ -77,14 +55,16 @@ module GPX
77
55
  @bounds.max_lon = pt.lon if pt.lon > @bounds.max_lon
78
56
  if last_pt
79
57
  @distance += haversine_distance(last_pt, pt)
80
- @duration += pt.time - last_pt.time if pt.time and last_pt.time
58
+ @duration += pt.time - last_pt.time if pt.time && last_pt.time
81
59
  end
82
60
  @points << pt
83
61
  end
84
62
 
85
63
  # Returns true if the given time is within this Segment.
86
64
  def contains_time?(time)
87
- (time >= @earliest_point.time and time <= @latest_point.time) rescue false
65
+ ((time >= @earliest_point.time) && (time <= @latest_point.time))
66
+ rescue StandardError
67
+ false
88
68
  end
89
69
 
90
70
  # Finds the closest point in time to the passed-in time argument. Useful
@@ -97,12 +77,12 @@ module GPX
97
77
  # Deletes all points within this Segment that lie outside of the given
98
78
  # area (which should be a Bounds object).
99
79
  def crop(area)
100
- delete_if { |pt| not area.contains?(pt) }
80
+ delete_if { |pt| !area.contains?(pt) }
101
81
  end
102
82
 
103
83
  # Deletes all points in this Segment that lie within the given area.
104
84
  def delete_area(area)
105
- delete_if{ |pt| area.contains?(pt) }
85
+ delete_if { |pt| area.contains?(pt) }
106
86
  end
107
87
 
108
88
  # A handy method that deletes points based on a block that is passed in.
@@ -114,18 +94,18 @@ module GPX
114
94
  keep_points = []
115
95
  last_pt = nil
116
96
  points.each do |pt|
117
- unless yield(pt)
118
- keep_points << pt
119
- update_meta_data(pt, last_pt)
120
- last_pt = pt
121
- end
97
+ next if yield(pt)
98
+
99
+ keep_points << pt
100
+ update_meta_data(pt, last_pt)
101
+ last_pt = pt
122
102
  end
123
103
  @points = keep_points
124
104
  end
125
105
 
126
106
  # Returns true if this Segment has no points.
127
107
  def empty?
128
- (points.nil? or (points.size == 0))
108
+ (points.nil? || points.empty?)
129
109
  end
130
110
 
131
111
  # Prints out a nice summary of this Segment.
@@ -133,31 +113,31 @@ module GPX
133
113
  result = "Track Segment\n"
134
114
  result << "\tSize: #{points.size} points\n"
135
115
  result << "\tDistance: #{distance} km\n"
136
- result << "\tEarliest Point: #{earliest_point.time.to_s} \n"
137
- result << "\tLatest Point: #{latest_point.time.to_s} \n"
116
+ result << "\tEarliest Point: #{earliest_point.time} \n"
117
+ result << "\tLatest Point: #{latest_point.time} \n"
138
118
  result << "\tLowest Point: #{lowest_point.elevation} \n"
139
119
  result << "\tHighest Point: #{highest_point.elevation}\n "
140
- result << "\tBounds: #{bounds.to_s}"
120
+ result << "\tBounds: #{bounds}"
141
121
  result
142
122
  end
143
123
 
144
124
  def find_point_by_time_or_offset(indicator)
145
125
  if indicator.nil?
146
- return nil
126
+ nil
147
127
  elsif indicator.is_a?(Integer)
148
- return closest_point(@earliest_point.time + indicator)
149
- elsif(indicator.is_a?(Time))
150
- return closest_point(indicator)
128
+ closest_point(@earliest_point.time + indicator)
129
+ elsif indicator.is_a?(Time)
130
+ closest_point(indicator)
151
131
  else
152
- raise Exception, "find_end_point_by_time_or_offset requires an argument of type Time or Integer"
132
+ raise ArgumentError, 'find_end_point_by_time_or_offset requires an argument of type Time or Integer'
153
133
  end
154
134
  end
155
-
135
+
156
136
  # smooths the location data in the segment (by recalculating the location as an average of 20 neighbouring points. Useful for removing noise from GPS traces.
157
- def smooth_location_by_average(opts={})
137
+ def smooth_location_by_average(opts = {})
158
138
  seconds_either_side = opts[:averaging_window] || 20
159
139
 
160
- #calculate the first and last points to which the smoothing should be applied
140
+ # calculate the first and last points to which the smoothing should be applied
161
141
  earliest = (find_point_by_time_or_offset(opts[:start]) || @earliest_point).time
162
142
  latest = (find_point_by_time_or_offset(opts[:end]) || @latest_point).time
163
143
 
@@ -165,18 +145,18 @@ module GPX
165
145
 
166
146
  @points.each do |point|
167
147
  if point.time > latest || point.time < earliest
168
- tmp_points.push point #add the point unaltered
169
- next
148
+ tmp_points.push point # add the point unaltered
149
+ next
170
150
  end
171
151
  lat_av = 0.to_f
172
152
  lon_av = 0.to_f
173
153
  alt_av = 0.to_f
174
154
  n = 0
175
- # k ranges from the time of the current point +/- 20s
176
- (-1*seconds_either_side..seconds_either_side).each do |k|
155
+ # k ranges from the time of the current point +/- 20s
156
+ (-1 * seconds_either_side..seconds_either_side).each do |k|
177
157
  # find the point nearest to the time offset indicated by k
178
158
  contributing_point = closest_point(point.time + k)
179
- #sum up the contributions to the average
159
+ # sum up the contributions to the average
180
160
  lat_av += contributing_point.lat
181
161
  lon_av += contributing_point.lon
182
162
  alt_av += contributing_point.elevation
@@ -184,37 +164,36 @@ module GPX
184
164
  end
185
165
  # calculate the averages
186
166
  tmp_point = point.clone
187
- tmp_point.lon = ((lon_av) / n).round(7)
188
- tmp_point.elevation = ((alt_av) / n).round(2)
189
- tmp_point.lat = ((lat_av) / n).round(7)
167
+ tmp_point.lon = (lon_av / n).round(7)
168
+ tmp_point.elevation = (alt_av / n).round(2)
169
+ tmp_point.lat = (lat_av / n).round(7)
190
170
  tmp_points.push tmp_point
191
171
  end
192
- last_pt = nil
193
172
  @points.clear
194
173
  reset_meta_data
195
- #now commit the averages back and recalculate the distances
174
+ # now commit the averages back and recalculate the distances
196
175
  tmp_points.each do |point|
197
176
  append_point(point)
198
177
  end
199
178
  end
200
179
 
201
180
  protected
181
+
202
182
  def find_closest(pts, time)
203
183
  return pts.first if pts.size == 1
204
- midpoint = pts.size/2
184
+
185
+ midpoint = pts.size / 2
205
186
  if pts.size == 2
206
187
  diff_1 = pts[0].time - time
207
188
  diff_2 = pts[1].time - time
208
189
  return (diff_1 < diff_2 ? pts[0] : pts[1])
209
190
  end
210
- if time >= pts[midpoint].time and time <= pts[midpoint+1].time
211
-
212
- return pts[midpoint]
213
-
214
- elsif(time <= pts[midpoint].time)
215
- return find_closest(pts[0..midpoint], time)
191
+ if (time >= pts[midpoint].time) && (time <= pts[midpoint + 1].time)
192
+ pts[midpoint]
193
+ elsif time <= pts[midpoint].time
194
+ find_closest(pts[0..midpoint], time)
216
195
  else
217
- return find_closest(pts[(midpoint+1)..-1], time)
196
+ find_closest(pts[(midpoint + 1)..], time)
218
197
  end
219
198
  end
220
199
 
@@ -224,11 +203,6 @@ module GPX
224
203
  p1.haversine_distance_from(p2)
225
204
  end
226
205
 
227
- # Calculate the plain Pythagorean difference between two points. Not currently used.
228
- def pythagorean_distance(p1, p2)
229
- p1.pythagorean_distance_from(p2)
230
- end
231
-
232
206
  # Calculates the distance between two points using the Law of Cosines formula. Not currently used.
233
207
  def law_of_cosines_distance(p1, p2)
234
208
  p1.law_of_cosines_distance_from(p2)
@@ -246,8 +220,8 @@ module GPX
246
220
 
247
221
  def update_meta_data(pt, last_pt)
248
222
  if pt.time
249
- @earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time)
250
- @latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time)
223
+ @earliest_point = pt if @earliest_point.nil? || (pt.time < @earliest_point.time)
224
+ @latest_point = pt if @latest_point.nil? || (pt.time > @latest_point.time)
251
225
  else
252
226
  # when no time information in data, we consider the points are ordered
253
227
  @earliest_point = @points[0]
@@ -255,16 +229,15 @@ module GPX
255
229
  end
256
230
 
257
231
  if pt.elevation
258
- @lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
259
- @highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
232
+ @lowest_point = pt if @lowest_point.nil? || (pt.elevation < @lowest_point.elevation)
233
+ @highest_point = pt if @highest_point.nil? || (pt.elevation > @highest_point.elevation)
260
234
  end
261
235
  @bounds.add(pt)
262
- if last_pt
263
- @distance += haversine_distance(last_pt, pt)
264
- @duration += pt.time - last_pt.time if pt.time and last_pt.time
265
- end
266
- end
267
236
 
268
- end
237
+ return unless last_pt
269
238
 
239
+ @distance += haversine_distance(last_pt, pt)
240
+ @duration += pt.time - last_pt.time if pt.time && last_pt.time
241
+ end
242
+ end
270
243
  end
data/lib/gpx/track.rb CHANGED
@@ -1,25 +1,5 @@
1
- #--
2
- # Copyright (c) 2006 Doug Fales
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #++
1
+ # frozen_string_literal: true
2
+
23
3
  module GPX
24
4
  # In GPX, a single Track can hold multiple Segments, each of which hold
25
5
  # multiple points (in this library, those points are instances of
@@ -34,28 +14,45 @@ module GPX
34
14
  # Initialize a track from a XML::Node, or, if no :element option is
35
15
  # passed, initialize a blank Track object.
36
16
  def initialize(opts = {})
17
+ super()
37
18
  @gpx_file = opts[:gpx_file]
38
19
  @segments = []
39
20
  @points = []
40
21
  reset_meta_data
41
- if(opts[:element])
42
- trk_element = opts[:element]
43
- @name = (trk_element.at("name").inner_text rescue "")
44
- @comment = (trk_element.at('cmt').inner_text rescue '')
45
- @description = (trk_element.at('desc').inner_text rescue '')
46
- trk_element.search("trkseg").each do |seg_element|
47
- seg = Segment.new(:element => seg_element, :track => self, :gpx_file => @gpx_file)
48
- update_meta_data(seg)
49
- @segments << seg
50
- end
22
+
23
+ return unless opts[:element]
24
+
25
+ trk_element = opts[:element]
26
+ @name = (
27
+ begin
28
+ trk_element.at('name').inner_text
29
+ rescue StandardError
30
+ ''
31
+ end)
32
+ @comment = (
33
+ begin
34
+ trk_element.at('cmt').inner_text
35
+ rescue StandardError
36
+ ''
37
+ end)
38
+ @description = (
39
+ begin
40
+ trk_element.at('desc').inner_text
41
+ rescue StandardError
42
+ ''
43
+ end)
44
+ trk_element.search('trkseg').each do |seg_element|
45
+ seg = Segment.new(element: seg_element, track: self, gpx_file: @gpx_file)
46
+ append_segment(seg)
51
47
  end
52
48
  end
53
49
 
54
50
  # Append a segment to this track, updating its meta data along the way.
55
51
  def append_segment(seg)
52
+ return if seg.points.empty?
53
+
56
54
  update_meta_data(seg)
57
55
  @segments << seg
58
- @points.concat(seg.points) unless seg.nil?
59
56
  end
60
57
 
61
58
  # Returns true if the given time occurs within any of the segments of this track.
@@ -63,7 +60,7 @@ module GPX
63
60
  segments.each do |seg|
64
61
  return true if seg.contains_time?(time)
65
62
  end
66
- return false
63
+ false
67
64
  end
68
65
 
69
66
  # Finds the closest point (to "time") within this track. Useful for
@@ -82,7 +79,7 @@ module GPX
82
79
  seg.crop(area)
83
80
  update_meta_data(seg) unless seg.empty?
84
81
  end
85
- segments.delete_if { |seg| seg.empty? }
82
+ segments.delete_if(&:empty?)
86
83
  end
87
84
 
88
85
  # Deletes all points within a given area and updates the meta data.
@@ -92,13 +89,13 @@ module GPX
92
89
  seg.delete_area(area)
93
90
  update_meta_data(seg) unless seg.empty?
94
91
  end
95
- segments.delete_if { |seg| seg.empty? }
92
+ segments.delete_if(&:empty?)
96
93
  end
97
94
 
98
95
  # Returns true if this track has no points in it. This should return
99
96
  # true even when the track has empty segments.
100
97
  def empty?
101
- (points.nil? or points.size.zero?)
98
+ (points.nil? || points.empty?)
102
99
  end
103
100
 
104
101
  # Prints out a friendly summary of this track (sans points). Useful for
@@ -115,7 +112,7 @@ module GPX
115
112
  result << "\tMoving duration: #{moving_duration} km\n"
116
113
  result << "\tLowest Point: #{lowest_point.elevation} \n"
117
114
  result << "\tHighest Point: #{highest_point.elevation}\n "
118
- result << "\tBounds: #{bounds.to_s}"
115
+ result << "\tBounds: #{bounds}"
119
116
  result
120
117
  end
121
118
 
@@ -125,12 +122,12 @@ module GPX
125
122
  @distance += seg.distance
126
123
  end
127
124
  end
128
-
125
+
129
126
  protected
130
127
 
131
128
  def update_meta_data(seg)
132
- @lowest_point = seg.lowest_point if(@lowest_point.nil? or seg.lowest_point.elevation < @lowest_point.elevation)
133
- @highest_point = seg.highest_point if(@highest_point.nil? or seg.highest_point.elevation > @highest_point.elevation)
129
+ @lowest_point = seg.lowest_point if @lowest_point.nil? || (seg.lowest_point.elevation < @lowest_point.elevation)
130
+ @highest_point = seg.highest_point if @highest_point.nil? || (seg.highest_point.elevation > @highest_point.elevation)
134
131
  @bounds.add(seg.bounds)
135
132
  @distance += seg.distance
136
133
  @moving_duration += seg.duration
@@ -145,6 +142,5 @@ module GPX
145
142
  @moving_duration = 0.0
146
143
  @points = []
147
144
  end
148
-
149
145
  end
150
146
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GPX
4
+ # Basically the same as a point, the TrackPoint class is supposed to
5
+ # represent the points that are children of Segment elements. So, the only
6
+ # real difference is that TrackPoints hold a reference to their parent
7
+ # Segments.
8
+ class TrackPoint < Point
9
+ RADIUS = 6371 # earth's mean radius in km
10
+
11
+ attr_accessor :segment
12
+
13
+ def initialize(opts = {})
14
+ super(opts)
15
+ @segment = opts[:segment]
16
+ end
17
+
18
+ # Units are in km
19
+ def haversine_distance_from(p2)
20
+ d_lat = p2.latr - latr
21
+ d_lon = p2.lonr - lonr
22
+ a = Math.sin(d_lat / 2) * Math.sin(d_lat / 2) + Math.cos(latr) * Math.cos(p2.latr) * Math.sin(d_lon / 2) * Math.sin(d_lon / 2)
23
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
24
+ RADIUS * c
25
+ end
26
+
27
+ # Units are in km
28
+ def law_of_cosines_distance_from(p2)
29
+ Math.acos(Math.sin(latr) * Math.sin(p2.latr) + Math.cos(latr) * Math.cos(p2.latr) * Math.cos(p2.lonr - lonr)) * RADIUS
30
+ end
31
+ end
32
+ end
data/lib/gpx/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GPX
2
- VERSION = "0.8.3"
4
+ VERSION = "1.1.1"
3
5
  end
data/lib/gpx/waypoint.rb CHANGED
@@ -1,56 +1,32 @@
1
- #--
2
- # Copyright (c) 2006 Doug Fales
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- #++
1
+ # frozen_string_literal: true
2
+
23
3
  module GPX
24
4
  # This class supports the concept of a waypoint. Beware that this class has
25
5
  # not seen much use yet, since WalkingBoss does not use waypoints right now.
26
6
  class Waypoint < Point
27
-
28
- SUB_ELEMENTS = %w{ele magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid extensions}
7
+ SUB_ELEMENTS = %w[ele magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid extensions].freeze
29
8
 
30
9
  attr_reader :gpx_file
10
+
31
11
  SUB_ELEMENTS.each { |sub_el| attr_accessor sub_el.to_sym }
32
12
 
33
13
  # Not implemented
34
- def crop(area)
35
- end
14
+ def crop(area); end
36
15
 
37
16
  # Not implemented
38
- def delete_area(area)
39
- end
17
+ def delete_area(area); end
40
18
 
41
19
  # Initializes a waypoint from a XML::Node.
42
20
  def initialize(opts = {})
43
- if(opts[:element] and opts[:gpx_file])
21
+ if opts[:element] && opts[:gpx_file]
44
22
  wpt_elem = opts[:element]
45
23
  @gpx_file = opts[:gpx_file]
46
- super(:element => wpt_elem, :gpx_file => @gpx_file)
24
+ super(element: wpt_elem, gpx_file: @gpx_file)
47
25
  instantiate_with_text_elements(wpt_elem, SUB_ELEMENTS)
48
26
  else
49
27
  opts.each do |key, value|
50
28
  assignment_method = "#{key}="
51
- if self.respond_to?(assignment_method)
52
- self.send(assignment_method, value)
53
- end
29
+ send(assignment_method, value) if respond_to?(assignment_method)
54
30
  end
55
31
  end
56
32
  end
@@ -65,7 +41,7 @@ module GPX
65
41
  result << "\tElevation: #{elevation}\n "
66
42
  result << "\tTime: #{time}\n"
67
43
  SUB_ELEMENTS.each do |sub_element_attribute|
68
- val = self.send(sub_element_attribute)
44
+ val = send(sub_element_attribute)
69
45
  result << "\t#{sub_element_attribute}: #{val}\n" unless val.nil?
70
46
  end
71
47
  result