gpx 0.7 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +82 -0
- data/Gemfile +4 -0
- data/README.md +100 -0
- data/Rakefile +10 -31
- data/bin/gpx_distance +10 -0
- data/bin/gpx_smooth +63 -0
- data/gpx.gemspec +17 -9
- data/lib/gpx.rb +16 -16
- data/lib/gpx/bounds.rb +43 -52
- data/lib/gpx/gpx.rb +17 -20
- data/lib/gpx/gpx_file.rb +292 -217
- data/lib/gpx/magellan_track_log.rb +105 -107
- data/lib/gpx/point.rb +58 -74
- data/lib/gpx/route.rb +31 -43
- data/lib/gpx/segment.rb +229 -198
- data/lib/gpx/track.rb +103 -109
- data/lib/gpx/trackpoint.rb +37 -12
- data/lib/gpx/version.rb +3 -0
- data/lib/gpx/waypoint.rb +3 -25
- data/tests/gpx10_test.rb +3 -3
- data/tests/gpx_file_test.rb +27 -25
- data/tests/gpx_files/arches.gpx +1 -1
- data/tests/gpx_files/big.gpx +1 -1
- data/tests/gpx_files/one_track.gpx +2 -1
- data/tests/gpx_files/waypoints.gpx +2 -0
- data/tests/magellan_test.rb +3 -3
- data/tests/output_test.rb +14 -17
- data/tests/route_test.rb +7 -7
- data/tests/segment_test.rb +40 -8
- data/tests/track_file_test.rb +3 -3
- data/tests/track_point_test.rb +30 -0
- data/tests/track_test.rb +20 -20
- data/tests/waypoint_test.rb +11 -7
- metadata +61 -10
- data/ChangeLog +0 -60
- data/Gemfile.lock +0 -23
- data/README.rdoc +0 -46
- data/tests/output/myoutput.gpx +0 -759
- data/tests/output/new_gpx_file_from_scratch.gpx +0 -105
data/lib/gpx/segment.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2006 Doug Fales
|
2
|
+
# Copyright (c) 2006 Doug Fales
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -21,202 +21,233 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
module GPX
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
24
|
+
# A segment is the basic container in a GPX file. A Segment contains points
|
25
|
+
# (in this lib, they're called TrackPoints). A Track contains Segments. An
|
26
|
+
# instance of Segment knows its highest point, lowest point, earliest and
|
27
|
+
# latest points, distance, and bounds.
|
28
|
+
class Segment < Base
|
29
|
+
|
30
|
+
attr_reader :earliest_point, :latest_point, :bounds, :highest_point, :lowest_point, :distance
|
31
|
+
attr_accessor :points, :track
|
32
|
+
|
33
|
+
# If a XML::Node object is passed-in, this will initialize a new
|
34
|
+
# Segment based on its contents. Otherwise, a blank Segment is created.
|
35
|
+
def initialize(opts = {})
|
36
|
+
@gpx_file = opts[:gpx_file]
|
37
|
+
@track = opts[:track]
|
38
|
+
@points = []
|
39
|
+
@earliest_point = nil
|
40
|
+
@latest_point = nil
|
41
|
+
@highest_point = nil
|
42
|
+
@lowest_point = nil
|
43
|
+
@distance = 0.0
|
44
|
+
@bounds = Bounds.new
|
45
|
+
if(opts[:element])
|
46
|
+
segment_element = opts[:element]
|
47
|
+
last_pt = nil
|
48
|
+
if segment_element.is_a?(Nokogiri::XML::Node)
|
49
|
+
segment_element.search("trkpt").each do |trkpt|
|
50
|
+
pt = TrackPoint.new(:element => trkpt, :segment => self, :gpx_file => @gpx_file)
|
51
|
+
append_point(pt)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Tack on a point to this Segment. All meta-data will be updated.
|
58
|
+
def append_point(pt)
|
59
|
+
last_pt = @points[-1]
|
60
|
+
unless pt.time.nil?
|
61
|
+
@earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time)
|
62
|
+
@latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time)
|
63
|
+
end
|
64
|
+
unless pt.elevation.nil?
|
65
|
+
@lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
|
66
|
+
@highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
|
67
|
+
end
|
68
|
+
@bounds.min_lat = pt.lat if pt.lat < @bounds.min_lat
|
69
|
+
@bounds.min_lon = pt.lon if pt.lon < @bounds.min_lon
|
70
|
+
@bounds.max_lat = pt.lat if pt.lat > @bounds.max_lat
|
71
|
+
@bounds.max_lon = pt.lon if pt.lon > @bounds.max_lon
|
72
|
+
@distance += haversine_distance(last_pt, pt) unless last_pt.nil?
|
73
|
+
@points << pt
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns true if the given time is within this Segment.
|
77
|
+
def contains_time?(time)
|
78
|
+
(time >= @earliest_point.time and time <= @latest_point.time) rescue false
|
79
|
+
end
|
80
|
+
|
81
|
+
# Finds the closest point in time to the passed-in time argument. Useful
|
82
|
+
# for matching up time-based objects (photos, video, etc) with a
|
83
|
+
# geographic location.
|
84
|
+
def closest_point(time)
|
85
|
+
find_closest(points, time)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Deletes all points within this Segment that lie outside of the given
|
89
|
+
# area (which should be a Bounds object).
|
90
|
+
def crop(area)
|
91
|
+
delete_if { |pt| not area.contains?(pt) }
|
92
|
+
end
|
93
|
+
|
94
|
+
# Deletes all points in this Segment that lie within the given area.
|
95
|
+
def delete_area(area)
|
96
|
+
delete_if{ |pt| area.contains?(pt) }
|
97
|
+
end
|
98
|
+
|
99
|
+
# A handy method that deletes points based on a block that is passed in.
|
100
|
+
# If the passed-in block returns true when given a point, then that point
|
101
|
+
# is deleted. For example:
|
102
|
+
# delete_if{ |pt| area.contains?(pt) }
|
103
|
+
def delete_if
|
104
|
+
reset_meta_data
|
105
|
+
keep_points = []
|
106
|
+
last_pt = nil
|
107
|
+
points.each do |pt|
|
108
|
+
unless yield(pt)
|
109
|
+
keep_points << pt
|
110
|
+
update_meta_data(pt, last_pt)
|
111
|
+
last_pt = pt
|
112
|
+
end
|
113
|
+
end
|
114
|
+
@points = keep_points
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns true if this Segment has no points.
|
118
|
+
def empty?
|
119
|
+
(points.nil? or (points.size == 0))
|
120
|
+
end
|
121
|
+
|
122
|
+
# Prints out a nice summary of this Segment.
|
123
|
+
def to_s
|
124
|
+
result = "Track Segment\n"
|
125
|
+
result << "\tSize: #{points.size} points\n"
|
126
|
+
result << "\tDistance: #{distance} km\n"
|
127
|
+
result << "\tEarliest Point: #{earliest_point.time.to_s} \n"
|
128
|
+
result << "\tLatest Point: #{latest_point.time.to_s} \n"
|
129
|
+
result << "\tLowest Point: #{lowest_point.elevation} \n"
|
130
|
+
result << "\tHighest Point: #{highest_point.elevation}\n "
|
131
|
+
result << "\tBounds: #{bounds.to_s}"
|
132
|
+
result
|
133
|
+
end
|
134
|
+
|
135
|
+
def find_point_by_time_or_offset(indicator)
|
136
|
+
if indicator.nil?
|
137
|
+
return nil
|
138
|
+
elsif indicator.is_a?(Integer)
|
139
|
+
return closest_point(@earliest_point.time + indicator)
|
140
|
+
elsif(indicator.is_a?(Time))
|
141
|
+
return closest_point(indicator)
|
142
|
+
else
|
143
|
+
raise Exception, "find_end_point_by_time_or_offset requires an argument of type Time or Integer"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# 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.
|
148
|
+
def smooth_location_by_average(opts={})
|
149
|
+
seconds_either_side = opts[:averaging_window] || 20
|
150
|
+
|
151
|
+
#calculate the first and last points to which the smoothing should be applied
|
152
|
+
earliest = (find_point_by_time_or_offset(opts[:start]) || @earliest_point).time
|
153
|
+
latest = (find_point_by_time_or_offset(opts[:end]) || @latest_point).time
|
154
|
+
|
155
|
+
tmp_points = []
|
156
|
+
|
157
|
+
@points.each do |point|
|
158
|
+
if point.time > latest || point.time < earliest
|
159
|
+
tmp_points.push point #add the point unaltered
|
160
|
+
next
|
161
|
+
end
|
162
|
+
lat_av = 0.to_f
|
163
|
+
lon_av = 0.to_f
|
164
|
+
alt_av = 0.to_f
|
165
|
+
n = 0
|
166
|
+
# k ranges from the time of the current point +/- 20s
|
167
|
+
(-1*seconds_either_side..seconds_either_side).each do |k|
|
168
|
+
# find the point nearest to the time offset indicated by k
|
169
|
+
contributing_point = closest_point(point.time + k)
|
170
|
+
#sum up the contributions to the average
|
171
|
+
lat_av += contributing_point.lat
|
172
|
+
lon_av += contributing_point.lon
|
173
|
+
alt_av += contributing_point.elevation
|
174
|
+
n += 1
|
175
|
+
end
|
176
|
+
# calculate the averages
|
177
|
+
tmp_point = point.clone
|
178
|
+
tmp_point.lon = ((lon_av) / n).round(7)
|
179
|
+
tmp_point.elevation = ((alt_av) / n).round(2)
|
180
|
+
tmp_point.lat = ((lat_av) / n).round(7)
|
181
|
+
tmp_points.push tmp_point
|
182
|
+
end
|
183
|
+
last_pt = nil
|
184
|
+
@distance = 0
|
185
|
+
@points.clear
|
186
|
+
reset_meta_data
|
187
|
+
#now commit the averages back and recalculate the distances
|
188
|
+
tmp_points.each do |point|
|
189
|
+
append_point(point)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
protected
|
194
|
+
def find_closest(pts, time)
|
195
|
+
return pts.first if pts.size == 1
|
196
|
+
midpoint = pts.size/2
|
197
|
+
if pts.size == 2
|
198
|
+
diff_1 = pts[0].time - time
|
199
|
+
diff_2 = pts[1].time - time
|
200
|
+
return (diff_1 < diff_2 ? pts[0] : pts[1])
|
201
|
+
end
|
202
|
+
if time >= pts[midpoint].time and time <= pts[midpoint+1].time
|
203
|
+
|
204
|
+
return pts[midpoint]
|
205
|
+
|
206
|
+
elsif(time <= pts[midpoint].time)
|
207
|
+
return find_closest(pts[0..midpoint], time)
|
208
|
+
else
|
209
|
+
return find_closest(pts[(midpoint+1)..-1], time)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Calculate the Haversine distance between two points. This is the method
|
214
|
+
# the library uses to calculate the cumulative distance of GPX files.
|
215
|
+
def haversine_distance(p1, p2)
|
216
|
+
p1.haversine_distance_from(p2)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Calculate the plain Pythagorean difference between two points. Not currently used.
|
220
|
+
def pythagorean_distance(p1, p2)
|
221
|
+
p1.pythagorean_distance_from(p2)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Calculates the distance between two points using the Law of Cosines formula. Not currently used.
|
225
|
+
def law_of_cosines_distance(p1, p2)
|
226
|
+
p1.law_of_cosines_distance_from(p2)
|
227
|
+
end
|
228
|
+
|
229
|
+
def reset_meta_data
|
230
|
+
@earliest_point = nil
|
231
|
+
@latest_point = nil
|
232
|
+
@highest_point = nil
|
233
|
+
@lowest_point = nil
|
234
|
+
@distance = 0.0
|
235
|
+
@bounds = Bounds.new
|
236
|
+
end
|
237
|
+
|
238
|
+
def update_meta_data(pt, last_pt)
|
239
|
+
unless pt.time.nil?
|
240
|
+
@earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time)
|
241
|
+
@latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time)
|
242
|
+
end
|
243
|
+
unless pt.elevation.nil?
|
244
|
+
@lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
|
245
|
+
@highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
|
246
|
+
end
|
247
|
+
@bounds.add(pt)
|
248
|
+
@distance += haversine_distance(last_pt, pt) unless last_pt.nil?
|
249
|
+
end
|
250
|
+
|
251
|
+
end
|
221
252
|
|
222
253
|
end
|
data/lib/gpx/track.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2006 Doug Fales
|
2
|
+
# Copyright (c) 2006 Doug Fales
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -21,129 +21,123 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
module GPX
|
24
|
+
# In GPX, a single Track can hold multiple Segments, each of which hold
|
25
|
+
# multiple points (in this library, those points are instances of
|
26
|
+
# TrackPoint). Each instance of this class has its own meta-data, including
|
27
|
+
# low point, high point, and distance. Of course, each track references an
|
28
|
+
# array of the segments that copmrise it, but additionally each track holds
|
29
|
+
# a reference to all of its points as one big array called "points".
|
30
|
+
class Track < Base
|
31
|
+
attr_reader :points, :bounds, :lowest_point, :highest_point, :distance
|
32
|
+
attr_accessor :segments, :name, :gpx_file
|
24
33
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@points = []
|
41
|
-
reset_meta_data
|
42
|
-
if(opts[:element])
|
43
|
-
trk_element = opts[:element]
|
44
|
-
@name = (trk_element.at("//name").inner_text rescue "")
|
45
|
-
trk_element.search("//trkseg").each do |seg_element|
|
46
|
-
seg = Segment.new(:element => seg_element, :track => self, :gpx_file => @gpx_file)
|
47
|
-
update_meta_data(seg)
|
48
|
-
@segments << seg
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Append a segment to this track, updating its meta data along the way.
|
54
|
-
def append_segment(seg)
|
55
|
-
update_meta_data(seg)
|
56
|
-
@segments << seg
|
57
|
-
@points.concat(seg.points) unless seg.nil?
|
34
|
+
# Initialize a track from a XML::Node, or, if no :element option is
|
35
|
+
# passed, initialize a blank Track object.
|
36
|
+
def initialize(opts = {})
|
37
|
+
@gpx_file = opts[:gpx_file]
|
38
|
+
@segments = []
|
39
|
+
@points = []
|
40
|
+
reset_meta_data
|
41
|
+
if(opts[:element])
|
42
|
+
trk_element = opts[:element]
|
43
|
+
@name = (trk_element.at("name").inner_text rescue "")
|
44
|
+
trk_element.search("trkseg").each do |seg_element|
|
45
|
+
seg = Segment.new(:element => seg_element, :track => self, :gpx_file => @gpx_file)
|
46
|
+
update_meta_data(seg)
|
47
|
+
@segments << seg
|
48
|
+
end
|
58
49
|
end
|
50
|
+
end
|
59
51
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
52
|
+
# Append a segment to this track, updating its meta data along the way.
|
53
|
+
def append_segment(seg)
|
54
|
+
update_meta_data(seg)
|
55
|
+
@segments << seg
|
56
|
+
@points.concat(seg.points) unless seg.nil?
|
57
|
+
end
|
67
58
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
segment = segments.select { |s| s.contains_time?(time) }
|
73
|
-
segment.first
|
59
|
+
# Returns true if the given time occurs within any of the segments of this track.
|
60
|
+
def contains_time?(time)
|
61
|
+
segments.each do |seg|
|
62
|
+
return true if seg.contains_time?(time)
|
74
63
|
end
|
64
|
+
return false
|
65
|
+
end
|
75
66
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
84
|
-
segments.delete_if { |seg| seg.empty? }
|
85
|
-
end
|
67
|
+
# Finds the closest point (to "time") within this track. Useful for
|
68
|
+
# correlating things like pictures, video, and other events, if you are
|
69
|
+
# working with a timestamp.
|
70
|
+
def closest_point(time)
|
71
|
+
segment = segments.select { |s| s.contains_time?(time) }
|
72
|
+
segment.first
|
73
|
+
end
|
86
74
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
segments.delete_if { |seg| seg.empty? }
|
75
|
+
# Removes all points outside of a given area and updates the meta data.
|
76
|
+
# The "area" paremeter is usually a Bounds object.
|
77
|
+
def crop(area)
|
78
|
+
reset_meta_data
|
79
|
+
segments.each do |seg|
|
80
|
+
seg.crop(area)
|
81
|
+
update_meta_data(seg) unless seg.empty?
|
95
82
|
end
|
83
|
+
segments.delete_if { |seg| seg.empty? }
|
84
|
+
end
|
96
85
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
86
|
+
# Deletes all points within a given area and updates the meta data.
|
87
|
+
def delete_area(area)
|
88
|
+
reset_meta_data
|
89
|
+
segments.each do |seg|
|
90
|
+
seg.delete_area(area)
|
91
|
+
update_meta_data(seg) unless seg.empty?
|
101
92
|
end
|
93
|
+
segments.delete_if { |seg| seg.empty? }
|
94
|
+
end
|
102
95
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
trk << name_elem
|
109
|
-
segments.each do |seg|
|
110
|
-
trk << seg.to_xml
|
111
|
-
end
|
112
|
-
trk
|
113
|
-
end
|
96
|
+
# Returns true if this track has no points in it. This should return
|
97
|
+
# true even when the track has empty segments.
|
98
|
+
def empty?
|
99
|
+
(points.nil? or points.size.zero?)
|
100
|
+
end
|
114
101
|
|
115
|
-
|
116
|
-
|
102
|
+
# Prints out a friendly summary of this track (sans points). Useful for
|
103
|
+
# debugging and sanity checks.
|
117
104
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
protected
|
105
|
+
def to_s
|
106
|
+
result = "Track \n"
|
107
|
+
result << "\tName: #{name}\n"
|
108
|
+
result << "\tSize: #{points.size} points\n"
|
109
|
+
result << "\tSegments: #{segments.size} \n"
|
110
|
+
result << "\tDistance: #{distance} km\n"
|
111
|
+
result << "\tLowest Point: #{lowest_point.elevation} \n"
|
112
|
+
result << "\tHighest Point: #{highest_point.elevation}\n "
|
113
|
+
result << "\tBounds: #{bounds.to_s}"
|
114
|
+
result
|
115
|
+
end
|
131
116
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
@distance += seg.distance
|
137
|
-
@points.concat(seg.points)
|
117
|
+
def recalculate_distance
|
118
|
+
@distance = 0
|
119
|
+
@segments.each do |seg|
|
120
|
+
@distance += seg.distance
|
138
121
|
end
|
122
|
+
end
|
123
|
+
|
124
|
+
protected
|
139
125
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
126
|
+
def update_meta_data(seg)
|
127
|
+
@lowest_point = seg.lowest_point if(@lowest_point.nil? or seg.lowest_point.elevation < @lowest_point.elevation)
|
128
|
+
@highest_point = seg.highest_point if(@highest_point.nil? or seg.highest_point.elevation > @highest_point.elevation)
|
129
|
+
@bounds.add(seg.bounds)
|
130
|
+
@distance += seg.distance
|
131
|
+
@points.concat(seg.points)
|
132
|
+
end
|
133
|
+
|
134
|
+
def reset_meta_data
|
135
|
+
@bounds = Bounds.new
|
136
|
+
@highest_point = nil
|
137
|
+
@lowest_point = nil
|
138
|
+
@distance = 0.0
|
139
|
+
@points = []
|
140
|
+
end
|
147
141
|
|
148
|
-
|
142
|
+
end
|
149
143
|
end
|