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/gpx.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,29 +21,26 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
module GPX
|
24
|
-
VERSION = "0.7"
|
25
|
-
|
26
24
|
# A common base class which provides a useful initializer method to many
|
27
25
|
# class in the GPX library.
|
28
26
|
class Base
|
27
|
+
# This initializer can take an XML::Node and scrape out any text
|
28
|
+
# elements with the names given in the "text_elements" array. Each
|
29
|
+
# element found underneath "parent" with a name in "text_elements" causes
|
30
|
+
# an attribute to be initialized on the instance. This means you don't
|
31
|
+
# have to pick out individual text elements in each initializer of each
|
32
|
+
# class (Route, TrackPoint, Track, etc). Just pass an array of possible
|
33
|
+
# attributes to this method.
|
34
|
+
def instantiate_with_text_elements(parent, text_elements)
|
35
|
+
text_elements.each do |el|
|
36
|
+
child_xpath = "#{el}"
|
37
|
+
unless parent.at(child_xpath).nil?
|
38
|
+
val = parent.at(child_xpath).inner_text
|
39
|
+
self.send("#{el}=", val)
|
40
|
+
end
|
41
|
+
end
|
29
42
|
|
30
|
-
|
31
|
-
# elements with the names given in the "text_elements" array. Each
|
32
|
-
# element found underneath "parent" with a name in "text_elements" causes
|
33
|
-
# an attribute to be initialized on the instance. This means you don't
|
34
|
-
# have to pick out individual text elements in each initializer of each
|
35
|
-
# class (Route, TrackPoint, Track, etc). Just pass an array of possible
|
36
|
-
# attributes to this method.
|
37
|
-
def instantiate_with_text_elements(parent, text_elements)
|
38
|
-
text_elements.each do |el|
|
39
|
-
child_xpath = "//#{el}"
|
40
|
-
unless parent.at(child_xpath).nil?
|
41
|
-
val = parent.at(child_xpath).inner_text
|
42
|
-
self.send("#{el}=", val)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
43
|
+
end
|
47
44
|
|
48
45
|
end
|
49
46
|
end
|
data/lib/gpx/gpx_file.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,250 +21,325 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
#++
|
23
23
|
module GPX
|
24
|
-
|
25
|
-
|
24
|
+
class GPXFile < Base
|
25
|
+
attr_accessor :tracks, :routes, :waypoints, :bounds, :lowest_point, :highest_point, :duration, :ns, :time, :name, :version, :creator, :description
|
26
26
|
|
27
|
+
DEFAULT_CREATOR = "GPX RubyGem #{GPX::VERSION} -- http://dougfales.github.io/gpx/".freeze
|
27
28
|
|
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
|
-
@xml = Hpricot(opts[:gpx_data])
|
58
|
-
end
|
59
|
-
# set XML namespace for XML find
|
60
|
-
#if @xml.root.namespaces.namespace
|
61
|
-
# @ns = 'gpx:' + @xml.root.namespaces.namespace.href
|
62
|
-
#else
|
63
|
-
# @ns = 'gpx:http://www.topografix.com/GPX/1/1' # default to GPX 1.1
|
64
|
-
#end
|
65
|
-
|
66
|
-
reset_meta_data
|
67
|
-
bounds_element = (@xml.at("//metadata/bounds") rescue nil)
|
68
|
-
if bounds_element
|
69
|
-
@bounds.min_lat = get_bounds_attr_value(bounds_element, %w{ min_lat minlat minLat })
|
70
|
-
@bounds.min_lon = get_bounds_attr_value(bounds_element, %w{ min_lon minlon minLon})
|
71
|
-
@bounds.max_lat = get_bounds_attr_value(bounds_element, %w{ max_lat maxlat maxLat})
|
72
|
-
@bounds.max_lon = get_bounds_attr_value(bounds_element, %w{ max_lon maxlon maxLon})
|
73
|
-
else
|
74
|
-
get_bounds = true
|
75
|
-
end
|
29
|
+
# This initializer can be used to create a new GPXFile from an existing
|
30
|
+
# file or to create a new GPXFile instance with no data (so that you can
|
31
|
+
# add tracks and points and write it out to a new file later).
|
32
|
+
# To read an existing GPX file, do this:
|
33
|
+
# gpx_file = GPXFile.new(:gpx_file => 'mygpxfile.gpx')
|
34
|
+
# puts "Speed: #{gpx_file.average_speed}"
|
35
|
+
# puts "Duration: #{gpx_file.duration}"
|
36
|
+
# puts "Bounds: #{gpx_file.bounds}"
|
37
|
+
#
|
38
|
+
# To read a GPX file from a string, use :gpx_data.
|
39
|
+
# gpx_file = GPXFile.new(:gpx_data => '<xml ...><gpx>...</gpx>)
|
40
|
+
# To create a new blank GPXFile instance:
|
41
|
+
# gpx_file = GPXFile.new
|
42
|
+
# Note that you can pass in any instance variables to this form of the initializer, including Tracks or Segments:
|
43
|
+
# some_track = get_track_from_csv('some_other_format.csv')
|
44
|
+
# gpx_file = GPXFile.new(:tracks => [some_track])
|
45
|
+
#
|
46
|
+
def initialize(opts = {})
|
47
|
+
@duration = 0
|
48
|
+
@attributes = {}
|
49
|
+
@namespace_defs = []
|
50
|
+
if(opts[:gpx_file] or opts[:gpx_data])
|
51
|
+
if opts[:gpx_file]
|
52
|
+
gpx_file = opts[:gpx_file]
|
53
|
+
gpx_file = File.open(gpx_file) unless gpx_file.is_a?(File)
|
54
|
+
@xml = Nokogiri::XML(gpx_file)
|
55
|
+
else
|
56
|
+
@xml = Nokogiri::XML(opts[:gpx_data])
|
57
|
+
end
|
76
58
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
59
|
+
gpx_element = @xml.at('gpx')
|
60
|
+
@attributes = gpx_element.attributes
|
61
|
+
@namespace_defs = gpx_element.namespace_definitions
|
62
|
+
#$stderr.puts gpx_element.attributes.sort.inspect
|
63
|
+
#$stderr.puts @xmlns.inspect
|
64
|
+
#$stderr.puts @xsi.inspect
|
65
|
+
@version = gpx_element['version']
|
66
|
+
reset_meta_data
|
67
|
+
bounds_element = (@xml.at("metadata/bounds") rescue nil)
|
68
|
+
if bounds_element
|
69
|
+
@bounds.min_lat = get_bounds_attr_value(bounds_element, %w{ min_lat minlat minLat })
|
70
|
+
@bounds.min_lon = get_bounds_attr_value(bounds_element, %w{ min_lon minlon minLon})
|
71
|
+
@bounds.max_lat = get_bounds_attr_value(bounds_element, %w{ max_lat maxlat maxLat})
|
72
|
+
@bounds.max_lon = get_bounds_attr_value(bounds_element, %w{ max_lon maxlon maxLon})
|
73
|
+
else
|
74
|
+
get_bounds = true
|
75
|
+
end
|
90
76
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
77
|
+
@time = Time.parse(@xml.at("metadata/time").inner_text) rescue nil
|
78
|
+
@name = @xml.at("metadata/name").inner_text rescue nil
|
79
|
+
@description = @xml.at('metadata/desc').inner_text rescue nil
|
80
|
+
@tracks = []
|
81
|
+
@xml.search("trk").each do |trk|
|
82
|
+
trk = Track.new(:element => trk, :gpx_file => self)
|
83
|
+
update_meta_data(trk, get_bounds)
|
84
|
+
@tracks << trk
|
85
|
+
end
|
86
|
+
@waypoints = []
|
87
|
+
@xml.search("wpt").each { |wpt| @waypoints << Waypoint.new(:element => wpt, :gpx_file => self) }
|
88
|
+
@routes = []
|
89
|
+
@xml.search("rte").each { |rte| @routes << Route.new(:element => rte, :gpx_file => self) }
|
90
|
+
@tracks.delete_if { |t| t.empty? }
|
104
91
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
92
|
+
calculate_duration
|
93
|
+
else
|
94
|
+
reset_meta_data
|
95
|
+
opts.each { |attr_name, value| instance_variable_set("@#{attr_name.to_s}", value) }
|
96
|
+
unless(@tracks.nil? or @tracks.size.zero?)
|
97
|
+
@tracks.each { |trk| update_meta_data(trk) }
|
98
|
+
calculate_duration
|
99
|
+
end
|
112
100
|
end
|
101
|
+
@tracks ||= []
|
102
|
+
@routes ||= []
|
103
|
+
@waypoints ||= []
|
104
|
+
end
|
113
105
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
return @distance
|
120
|
-
when /meters/i
|
121
|
-
return (@distance * 1000)
|
122
|
-
when /miles/i
|
123
|
-
return (@distance * 0.62)
|
124
|
-
end
|
106
|
+
def get_bounds_attr_value(el, possible_names)
|
107
|
+
result = nil
|
108
|
+
possible_names.each do |name|
|
109
|
+
result = el[name]
|
110
|
+
break unless result.nil?
|
125
111
|
end
|
112
|
+
return (result.to_f rescue nil)
|
113
|
+
end
|
126
114
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
return (@distance * 0.62) / (@duration/3600.0)
|
138
|
-
end
|
115
|
+
# Returns the distance, in kilometers, meters, or miles, of all of the
|
116
|
+
# tracks and segments contained in this GPXFile.
|
117
|
+
def distance(opts = { :units => 'kilometers' })
|
118
|
+
case opts[:units]
|
119
|
+
when /kilometers/i
|
120
|
+
return @distance
|
121
|
+
when /meters/i
|
122
|
+
return (@distance * 1000)
|
123
|
+
when /miles/i
|
124
|
+
return (@distance * 0.62)
|
139
125
|
end
|
126
|
+
end
|
140
127
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
keep_tracks << trk
|
153
|
-
end
|
154
|
-
end
|
155
|
-
@tracks = keep_tracks
|
156
|
-
routes.each { |rte| rte.crop(area) }
|
157
|
-
waypoints.each { |wpt| wpt.crop(area) }
|
128
|
+
# Returns the average speed, in km/hr, meters/hr, or miles/hr, of this
|
129
|
+
# GPXFile. The calculation is based on the total distance divided by the
|
130
|
+
# total duration of the entire file.
|
131
|
+
def average_speed(opts = { :units => 'kilometers' })
|
132
|
+
case opts[:units]
|
133
|
+
when /kilometers/i
|
134
|
+
return @distance / (@duration/3600.0)
|
135
|
+
when /meters/i
|
136
|
+
return (@distance * 1000) / (@duration/3600.0)
|
137
|
+
when /miles/i
|
138
|
+
return (@distance * 0.62) / (@duration/3600.0)
|
158
139
|
end
|
140
|
+
end
|
159
141
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
update_meta_data(trk)
|
174
|
-
keep_tracks << trk
|
175
|
-
end
|
176
|
-
end
|
177
|
-
@tracks = keep_tracks
|
178
|
-
routes.each { |rte| rte.delete_area(area) }
|
179
|
-
waypoints.each { |wpt| wpt.delete_area(area) }
|
142
|
+
# Crops any points falling within a rectangular area. Identical to the
|
143
|
+
# delete_area method in every respect except that the points outside of
|
144
|
+
# the given area are deleted. Note that this method automatically causes
|
145
|
+
# the meta data to be updated after deletion.
|
146
|
+
def crop(area)
|
147
|
+
reset_meta_data
|
148
|
+
keep_tracks = []
|
149
|
+
tracks.each do |trk|
|
150
|
+
trk.crop(area)
|
151
|
+
unless trk.empty?
|
152
|
+
update_meta_data(trk)
|
153
|
+
keep_tracks << trk
|
154
|
+
end
|
180
155
|
end
|
156
|
+
@tracks = keep_tracks
|
157
|
+
routes.each { |rte| rte.crop(area) }
|
158
|
+
waypoints.each { |wpt| wpt.crop(area) }
|
159
|
+
end
|
181
160
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
161
|
+
# Deletes any points falling within a rectangular area. The "area"
|
162
|
+
# parameter is usually an instance of the Bounds class. Note that this
|
163
|
+
# method cascades into similarly named methods of subordinate classes
|
164
|
+
# (i.e. Track, Segment), which means, if you want the deletion to apply
|
165
|
+
# to all the data, you only call this one (and not the one in Track or
|
166
|
+
# Segment classes). Note that this method automatically causes the meta
|
167
|
+
# data to be updated after deletion.
|
168
|
+
def delete_area(area)
|
169
|
+
reset_meta_data
|
170
|
+
keep_tracks = []
|
171
|
+
tracks.each do |trk|
|
172
|
+
trk.delete_area(area)
|
173
|
+
unless trk.empty?
|
174
|
+
update_meta_data(trk)
|
175
|
+
keep_tracks << trk
|
176
|
+
end
|
189
177
|
end
|
178
|
+
@tracks = keep_tracks
|
179
|
+
routes.each { |rte| rte.delete_area(area) }
|
180
|
+
waypoints.each { |wpt| wpt.delete_area(area) }
|
181
|
+
end
|
190
182
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
@distance += trk.distance
|
200
|
-
end
|
183
|
+
# Resets the meta data for this GPX file. Meta data includes the bounds,
|
184
|
+
# the high and low points, and the distance.
|
185
|
+
def reset_meta_data
|
186
|
+
@bounds = Bounds.new
|
187
|
+
@highest_point = nil
|
188
|
+
@lowest_point = nil
|
189
|
+
@distance = 0.0
|
190
|
+
end
|
201
191
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
192
|
+
# Updates the meta data for this GPX file. Meta data includes the
|
193
|
+
# bounds, the high and low points, and the distance. This is useful when
|
194
|
+
# you modify the GPX data (i.e. by adding or deleting points) and you
|
195
|
+
# want the meta data to accurately reflect the new data.
|
196
|
+
def update_meta_data(trk, get_bounds = true)
|
197
|
+
@lowest_point = trk.lowest_point if(@lowest_point.nil? or (!trk.lowest_point.nil? and trk.lowest_point.elevation < @lowest_point.elevation))
|
198
|
+
@highest_point = trk.highest_point if(@highest_point.nil? or (!trk.highest_point.nil? and trk.highest_point.elevation > @highest_point.elevation))
|
199
|
+
@bounds.add(trk.bounds) if get_bounds
|
200
|
+
@distance += trk.distance
|
201
|
+
end
|
202
|
+
|
203
|
+
# Serialize the current GPXFile to a gpx file named <filename>.
|
204
|
+
# If the file does not exist, it is created. If it does exist, it is overwritten.
|
205
|
+
def write(filename, update_time = true)
|
206
|
+
@time = Time.now if(@time.nil? or update_time)
|
207
|
+
@name ||= File.basename(filename)
|
208
|
+
doc = generate_xml_doc
|
209
|
+
File.open(filename, 'w+') { |f| f.write(doc.to_xml) }
|
210
|
+
end
|
211
|
+
|
212
|
+
def to_s(update_time = true)
|
213
|
+
@time = Time.now if(@time.nil? or update_time)
|
214
|
+
doc = generate_xml_doc
|
215
|
+
doc.to_xml
|
216
|
+
end
|
217
|
+
|
218
|
+
def inspect
|
219
|
+
"<#{self.class.name}:...>"
|
220
|
+
end
|
221
|
+
|
222
|
+
def recalculate_distance
|
223
|
+
@distance = 0
|
224
|
+
@tracks.each do |track|
|
225
|
+
track.recalculate_distance
|
226
|
+
@distance += track.distance
|
209
227
|
end
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
def attributes_and_nsdefs_as_gpx_attributes
|
232
|
+
#$stderr.puts @namespace_defs.inspect
|
233
|
+
gpx_header = {}
|
234
|
+
@attributes.each do |k,v|
|
235
|
+
k = v.namespace.prefix + ':' + k if v.namespace
|
236
|
+
gpx_header[k] = v.value
|
237
|
+
end
|
210
238
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
239
|
+
@namespace_defs.each do |nsd|
|
240
|
+
tag = 'xmlns'
|
241
|
+
if nsd.prefix
|
242
|
+
tag += ':' + nsd.prefix
|
243
|
+
end
|
244
|
+
gpx_header[tag] = nsd.href
|
215
245
|
end
|
246
|
+
return gpx_header
|
247
|
+
end
|
248
|
+
|
249
|
+
def generate_xml_doc
|
250
|
+
@version ||= '1.1'
|
251
|
+
version_dir = version.gsub('.','/')
|
216
252
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
gpx_elem['xmlns'] = @ns || "http://www.topografix.com/GPX/#{version_dir}"
|
226
|
-
gpx_elem['version'] = "#{@version}"
|
227
|
-
gpx_elem['creator'] = "GPX RubyGem #{GPX::VERSION} Copyright 2006-2009 Doug Fales -- http://gpx.rubyforge.org/"
|
228
|
-
gpx_elem['xsi:schemaLocation'] = "http://www.topografix.com/GPX/#{version_dir} http://www.topografix.com/GPX/#{version_dir}/gpx.xsd"
|
253
|
+
gpx_header = attributes_and_nsdefs_as_gpx_attributes
|
254
|
+
|
255
|
+
gpx_header['version'] = @version.to_s if !gpx_header['version']
|
256
|
+
gpx_header['creator'] = DEFAULT_CREATOR if !gpx_header['creator']
|
257
|
+
gpx_header['xsi:schemaLocation'] = "http://www.topografix.com/GPX/#{version_dir} http://www.topografix.com/GPX/#{version_dir}/gpx.xsd" if !gpx_header['xsi:schemaLocation']
|
258
|
+
gpx_header['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance" if !gpx_header['xsi'] and !gpx_header['xmlns:xsi']
|
259
|
+
|
260
|
+
#$stderr.puts gpx_header.keys.inspect
|
229
261
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
262
|
+
doc = Nokogiri::XML::Builder.new do |xml|
|
263
|
+
xml.gpx(gpx_header) \
|
264
|
+
{
|
265
|
+
# version 1.0 of the schema doesn't support the metadata element, so push them straight to the root 'gpx' element
|
266
|
+
if (@version == '1.0') then
|
267
|
+
xml.name @name
|
268
|
+
xml.time @time.xmlschema
|
269
|
+
xml.bound(
|
270
|
+
minlat: bounds.min_lat,
|
271
|
+
minlon: bounds.min_lon,
|
272
|
+
maxlat: bounds.max_lat,
|
273
|
+
maxlon: bounds.max_lon,
|
274
|
+
)
|
275
|
+
else
|
276
|
+
xml.metadata {
|
277
|
+
xml.name @name
|
278
|
+
xml.time @time.xmlschema
|
279
|
+
xml.bound(
|
280
|
+
minlat: bounds.min_lat,
|
281
|
+
minlon: bounds.min_lon,
|
282
|
+
maxlat: bounds.max_lat,
|
283
|
+
maxlon: bounds.max_lon,
|
284
|
+
)
|
285
|
+
}
|
286
|
+
end
|
235
287
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
gpx_elem << time_elem
|
240
|
-
gpx_elem << bounds.to_xml
|
241
|
-
else
|
242
|
-
meta_data_elem = Node.new('metadata')
|
243
|
-
meta_data_elem << name_elem
|
244
|
-
meta_data_elem << time_elem
|
245
|
-
meta_data_elem << bounds.to_xml
|
246
|
-
gpx_elem << meta_data_elem
|
247
|
-
end
|
288
|
+
tracks.each do |t|
|
289
|
+
xml.trk {
|
290
|
+
xml.name t.name
|
248
291
|
|
249
|
-
|
250
|
-
|
251
|
-
|
292
|
+
t.segments.each do |seg|
|
293
|
+
xml.trkseg {
|
294
|
+
seg.points.each do |p|
|
295
|
+
xml.trkpt(lat: p.lat, lon: p.lon) {
|
296
|
+
xml.time p.time.xmlschema unless p.time.nil?
|
297
|
+
xml.ele p.elevation unless p.elevation.nil?
|
298
|
+
xml << p.extensions.to_xml unless p.extensions.nil?
|
299
|
+
}
|
300
|
+
end
|
301
|
+
}
|
302
|
+
end
|
303
|
+
}
|
304
|
+
end unless tracks.nil?
|
252
305
|
|
253
|
-
|
254
|
-
|
306
|
+
waypoints.each do |w|
|
307
|
+
xml.wpt(lat: w.lat, lon: w.lon) {
|
308
|
+
xml.time w.time.xmlschema unless w.time.nil?
|
309
|
+
Waypoint::SUB_ELEMENTS.each do |sub_elem|
|
310
|
+
xml.send(sub_elem, w.send(sub_elem)) if w.respond_to?(sub_elem) && !w.send(sub_elem).nil?
|
311
|
+
end
|
312
|
+
}
|
313
|
+
end unless waypoints.nil?
|
255
314
|
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
315
|
+
routes.each do |r|
|
316
|
+
xml.rte {
|
317
|
+
xml.name r.name
|
318
|
+
|
319
|
+
r.points.each do |p|
|
320
|
+
xml.rtept(lat: p.lat, lon: p.lon) {
|
321
|
+
xml.time p.time.xmlschema unless p.time.nil?
|
322
|
+
xml.ele p.elevation unless p.elevation.nil?
|
323
|
+
}
|
324
|
+
end
|
325
|
+
}
|
326
|
+
end unless routes.nil?
|
327
|
+
}
|
266
328
|
end
|
267
329
|
|
330
|
+
return doc
|
331
|
+
end
|
268
332
|
|
269
|
-
|
333
|
+
# Calculates and sets the duration attribute by subtracting the time on
|
334
|
+
# the very first point from the time on the very last point.
|
335
|
+
def calculate_duration
|
336
|
+
@duration = 0
|
337
|
+
if(@tracks.nil? or @tracks.size.zero? or @tracks[0].segments.nil? or @tracks[0].segments.size.zero?)
|
338
|
+
return @duration
|
339
|
+
end
|
340
|
+
@duration = (@tracks[-1].segments[-1].points[-1].time - @tracks.first.segments.first.points.first.time)
|
341
|
+
rescue
|
342
|
+
@duration = 0
|
343
|
+
end
|
344
|
+
end
|
270
345
|
end
|