gpx 0.8.3 → 1.1.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.
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/gpx_file.rb CHANGED
@@ -1,30 +1,11 @@
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
  class GPXFile < Base
25
- attr_accessor :tracks, :routes, :waypoints, :bounds, :lowest_point, :highest_point, :duration, :ns, :time, :name, :version, :creator, :description, :moving_duration
5
+ attr_accessor :tracks,
6
+ :routes, :waypoints, :bounds, :lowest_point, :highest_point, :duration, :ns, :time, :name, :version, :creator, :description, :moving_duration
26
7
 
27
- DEFAULT_CREATOR = "GPX RubyGem #{GPX::VERSION} -- http://dougfales.github.io/gpx/".freeze
8
+ DEFAULT_CREATOR = "GPX RubyGem #{GPX::VERSION} -- http://dougfales.github.io/gpx/"
28
9
 
29
10
  # This initializer can be used to create a new GPXFile from an existing
30
11
  # file or to create a new GPXFile instance with no data (so that you can
@@ -44,10 +25,14 @@ module GPX
44
25
  # gpx_file = GPXFile.new(:tracks => [some_track])
45
26
  #
46
27
  def initialize(opts = {})
28
+ super()
47
29
  @duration = 0
48
30
  @attributes = {}
49
31
  @namespace_defs = []
50
- if(opts[:gpx_file] or opts[:gpx_data])
32
+ @tracks = []
33
+ @time = nil
34
+
35
+ if opts[:gpx_file] || opts[:gpx_data]
51
36
  if opts[:gpx_file]
52
37
  gpx_file = opts[:gpx_file]
53
38
  gpx_file = File.open(gpx_file) unless gpx_file.is_a?(File)
@@ -59,41 +44,54 @@ module GPX
59
44
  gpx_element = @xml.at('gpx')
60
45
  @attributes = gpx_element.attributes
61
46
  @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
47
  @version = gpx_element['version']
66
48
  reset_meta_data
67
- bounds_element = (@xml.at("metadata/bounds") rescue nil)
49
+ bounds_element = (
50
+ begin
51
+ @xml.at('metadata/bounds')
52
+ rescue StandardError
53
+ nil
54
+ end)
68
55
  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})
56
+ @bounds.min_lat = get_bounds_attr_value(bounds_element, %w[min_lat minlat minLat])
57
+ @bounds.min_lon = get_bounds_attr_value(bounds_element, %w[min_lon minlon minLon])
58
+ @bounds.max_lat = get_bounds_attr_value(bounds_element, %w[max_lat maxlat maxLat])
59
+ @bounds.max_lon = get_bounds_attr_value(bounds_element, %w[max_lon maxlon maxLon])
73
60
  else
74
61
  get_bounds = true
75
62
  end
76
63
 
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)
64
+ @time = begin
65
+ Time.parse(@xml.at('metadata/time').inner_text)
66
+ rescue StandardError
67
+ nil
68
+ end
69
+ @name = begin
70
+ @xml.at('metadata/name').inner_text
71
+ rescue StandardError
72
+ nil
73
+ end
74
+ @description = begin
75
+ @xml.at('metadata/desc').inner_text
76
+ rescue StandardError
77
+ nil
78
+ end
79
+ @xml.search('trk').each do |trk|
80
+ trk = Track.new(element: trk, gpx_file: self)
83
81
  update_meta_data(trk, get_bounds)
84
82
  @tracks << trk
85
83
  end
86
84
  @waypoints = []
87
- @xml.search("wpt").each { |wpt| @waypoints << Waypoint.new(:element => wpt, :gpx_file => self) }
85
+ @xml.search('wpt').each { |wpt| @waypoints << Waypoint.new(element: wpt, gpx_file: self) }
88
86
  @routes = []
89
- @xml.search("rte").each { |rte| @routes << Route.new(:element => rte, :gpx_file => self) }
90
- @tracks.delete_if { |t| t.empty? }
87
+ @xml.search('rte').each { |rte| @routes << Route.new(element: rte, gpx_file: self) }
88
+ @tracks.delete_if(&:empty?)
91
89
 
92
90
  calculate_duration
93
91
  else
94
92
  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?)
93
+ opts.each { |attr_name, value| instance_variable_set("@#{attr_name}", value) }
94
+ unless @tracks.nil? || @tracks.empty?
97
95
  @tracks.each { |trk| update_meta_data(trk) }
98
96
  calculate_duration
99
97
  end
@@ -109,19 +107,24 @@ module GPX
109
107
  result = el[name]
110
108
  break unless result.nil?
111
109
  end
112
- return (result.to_f rescue nil)
110
+ (
111
+ begin
112
+ result.to_f
113
+ rescue StandardError
114
+ nil
115
+ end)
113
116
  end
114
117
 
115
118
  # Returns the distance, in kilometers, meters, or miles, of all of the
116
119
  # tracks and segments contained in this GPXFile.
117
- def distance(opts = { :units => 'kilometers' })
120
+ def distance(opts = { units: 'kilometers' })
118
121
  case opts[:units]
119
122
  when /kilometers/i
120
- return @distance
123
+ @distance
121
124
  when /meters/i
122
- return (@distance * 1000)
125
+ (@distance * 1000)
123
126
  when /miles/i
124
- return (@distance * 0.62)
127
+ (@distance * 0.62)
125
128
  end
126
129
  end
127
130
 
@@ -129,14 +132,14 @@ module GPX
129
132
  # GPXFile. The calculation is based on the total distance divided by the
130
133
  # sum of duration of all segments of all tracks
131
134
  # (not taking into accounting pause time).
132
- def average_speed(opts = { :units => 'kilometers' })
135
+ def average_speed(opts = { units: 'kilometers' })
133
136
  case opts[:units]
134
137
  when /kilometers/i
135
- return distance / (moving_duration/3600.0)
138
+ distance / (moving_duration / 3600.0)
136
139
  when /meters/i
137
- return (distance * 1000) / (moving_duration/3600.0)
140
+ (distance * 1000) / (moving_duration / 3600.0)
138
141
  when /miles/i
139
- return (distance * 0.62) / (moving_duration/3600.0)
142
+ (distance * 0.62) / (moving_duration / 3600.0)
140
143
  end
141
144
  end
142
145
 
@@ -176,7 +179,7 @@ module GPX
176
179
  keep_tracks << trk
177
180
  end
178
181
  end
179
- @tracks = keep_tracks
182
+ @tracks = keep_tracks
180
183
  routes.each { |rte| rte.delete_area(area) }
181
184
  waypoints.each { |wpt| wpt.delete_area(area) }
182
185
  end
@@ -191,13 +194,15 @@ module GPX
191
194
  @moving_duration = 0.0
192
195
  end
193
196
 
197
+ # rubocop:disable Style/OptionalBooleanParameter
198
+
194
199
  # Updates the meta data for this GPX file. Meta data includes the
195
200
  # bounds, the high and low points, and the distance. This is useful when
196
201
  # you modify the GPX data (i.e. by adding or deleting points) and you
197
202
  # want the meta data to accurately reflect the new data.
198
203
  def update_meta_data(trk, get_bounds = true)
199
- @lowest_point = trk.lowest_point if(@lowest_point.nil? or (!trk.lowest_point.nil? and trk.lowest_point.elevation < @lowest_point.elevation))
200
- @highest_point = trk.highest_point if(@highest_point.nil? or (!trk.highest_point.nil? and trk.highest_point.elevation > @highest_point.elevation))
204
+ @lowest_point = trk.lowest_point if @lowest_point.nil? || (!trk.lowest_point.nil? && (trk.lowest_point.elevation < @lowest_point.elevation))
205
+ @highest_point = trk.highest_point if @highest_point.nil? || (!trk.highest_point.nil? && (trk.highest_point.elevation > @highest_point.elevation))
201
206
  @bounds.add(trk.bounds) if get_bounds
202
207
  @distance += trk.distance
203
208
  @moving_duration += trk.moving_duration
@@ -206,17 +211,18 @@ module GPX
206
211
  # Serialize the current GPXFile to a gpx file named <filename>.
207
212
  # If the file does not exist, it is created. If it does exist, it is overwritten.
208
213
  def write(filename, update_time = true)
209
- @time = Time.now if(@time.nil? or update_time)
214
+ @time = Time.now if @time.nil? || update_time
210
215
  @name ||= File.basename(filename)
211
216
  doc = generate_xml_doc
212
217
  File.open(filename, 'w+') { |f| f.write(doc.to_xml) }
213
218
  end
214
219
 
215
220
  def to_s(update_time = true)
216
- @time = Time.now if(@time.nil? or update_time)
221
+ @time = Time.now if @time.nil? || update_time
217
222
  doc = generate_xml_doc
218
223
  doc.to_xml
219
224
  end
225
+ # rubocop:enable Style/OptionalBooleanParameter
220
226
 
221
227
  def inspect
222
228
  "<#{self.class.name}:...>"
@@ -231,117 +237,117 @@ module GPX
231
237
  end
232
238
 
233
239
  private
240
+
234
241
  def attributes_and_nsdefs_as_gpx_attributes
235
- #$stderr.puts @namespace_defs.inspect
242
+ # $stderr.puts @namespace_defs.inspect
236
243
  gpx_header = {}
237
- @attributes.each do |k,v|
238
- k = v.namespace.prefix + ':' + k if v.namespace
244
+ @attributes.each do |k, v|
245
+ k = "#{v.namespace.prefix}:#{k}" if v.namespace
239
246
  gpx_header[k] = v.value
240
- end
247
+ end
241
248
 
242
249
  @namespace_defs.each do |nsd|
243
250
  tag = 'xmlns'
244
- if nsd.prefix
245
- tag += ':' + nsd.prefix
246
- end
251
+ tag += ":#{nsd.prefix}" if nsd.prefix
247
252
  gpx_header[tag] = nsd.href
248
253
  end
249
- return gpx_header
254
+ gpx_header
250
255
  end
251
-
256
+
252
257
  def generate_xml_doc
253
258
  @version ||= '1.1'
254
- version_dir = version.gsub('.','/')
259
+ version_dir = version.tr('.', '/')
255
260
 
256
261
  gpx_header = attributes_and_nsdefs_as_gpx_attributes
257
-
258
- gpx_header['version'] = @version.to_s if !gpx_header['version']
259
- gpx_header['creator'] = DEFAULT_CREATOR if !gpx_header['creator']
260
- 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']
261
- gpx_header['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance" if !gpx_header['xsi'] and !gpx_header['xmlns:xsi']
262
-
263
- #$stderr.puts gpx_header.keys.inspect
264
-
265
- doc = Nokogiri::XML::Builder.new do |xml|
266
- xml.gpx(gpx_header) \
267
- {
268
- # version 1.0 of the schema doesn't support the metadata element, so push them straight to the root 'gpx' element
269
- if (@version == '1.0') then
262
+
263
+ gpx_header['version'] = @version.to_s unless gpx_header['version']
264
+ gpx_header['creator'] = DEFAULT_CREATOR unless gpx_header['creator']
265
+ gpx_header['xsi:schemaLocation'] = "http://www.topografix.com/GPX/#{version_dir} http://www.topografix.com/GPX/#{version_dir}/gpx.xsd" unless gpx_header['xsi:schemaLocation']
266
+ gpx_header['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance' if !gpx_header['xsi'] && !gpx_header['xmlns:xsi']
267
+
268
+ # $stderr.puts gpx_header.keys.inspect
269
+
270
+ # rubocop:disable Metrics/BlockLength
271
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
272
+ xml.gpx(gpx_header) do
273
+ # version 1.0 of the schema doesn't support the metadata element, so push them straight to the root 'gpx' element
274
+ if @version == '1.0'
275
+ xml.name @name
276
+ xml.time @time.xmlschema
277
+ xml.bound(
278
+ minlat: bounds.min_lat,
279
+ minlon: bounds.min_lon,
280
+ maxlat: bounds.max_lat,
281
+ maxlon: bounds.max_lon
282
+ )
283
+ else
284
+ xml.metadata do
270
285
  xml.name @name
271
286
  xml.time @time.xmlschema
272
287
  xml.bound(
273
288
  minlat: bounds.min_lat,
274
289
  minlon: bounds.min_lon,
275
290
  maxlat: bounds.max_lat,
276
- maxlon: bounds.max_lon,
291
+ maxlon: bounds.max_lon
277
292
  )
278
- else
279
- xml.metadata {
280
- xml.name @name
281
- xml.time @time.xmlschema
282
- xml.bound(
283
- minlat: bounds.min_lat,
284
- minlon: bounds.min_lon,
285
- maxlat: bounds.max_lat,
286
- maxlon: bounds.max_lon,
287
- )
288
- }
289
293
  end
294
+ end
295
+
296
+ tracks&.each do |t|
297
+ xml.trk do
298
+ xml.name t.name
290
299
 
291
- tracks.each do |t|
292
- xml.trk {
293
- xml.name t.name
294
-
295
- t.segments.each do |seg|
296
- xml.trkseg {
297
- seg.points.each do |p|
298
- xml.trkpt(lat: p.lat, lon: p.lon) {
299
- xml.time p.time.xmlschema unless p.time.nil?
300
- xml.ele p.elevation unless p.elevation.nil?
301
- xml << p.extensions.to_xml unless p.extensions.nil?
302
- }
300
+ t.segments.each do |seg|
301
+ xml.trkseg do
302
+ seg.points.each do |p|
303
+ xml.trkpt(lat: p.lat, lon: p.lon) do
304
+ xml.time p.time.xmlschema unless p.time.nil?
305
+ xml.ele p.elevation unless p.elevation.nil?
306
+ xml << p.extensions.to_xml unless p.extensions.nil?
303
307
  end
304
- }
308
+ end
305
309
  end
306
- }
307
- end unless tracks.nil?
308
-
309
- waypoints.each do |w|
310
- xml.wpt(lat: w.lat, lon: w.lon) {
311
- xml.time w.time.xmlschema unless w.time.nil?
312
- Waypoint::SUB_ELEMENTS.each do |sub_elem|
313
- xml.send(sub_elem, w.send(sub_elem)) if w.respond_to?(sub_elem) && !w.send(sub_elem).nil?
314
- end
315
- }
316
- end unless waypoints.nil?
317
-
318
- routes.each do |r|
319
- xml.rte {
320
- xml.name r.name
321
-
322
- r.points.each do |p|
323
- xml.rtept(lat: p.lat, lon: p.lon) {
324
- xml.time p.time.xmlschema unless p.time.nil?
325
- xml.ele p.elevation unless p.elevation.nil?
326
- }
310
+ end
311
+ end
312
+ end
313
+
314
+ waypoints&.each do |w|
315
+ xml.wpt(lat: w.lat, lon: w.lon) do
316
+ xml.time w.time.xmlschema unless w.time.nil?
317
+ Waypoint::SUB_ELEMENTS.each do |sub_elem|
318
+ xml.send(sub_elem, w.send(sub_elem)) if w.respond_to?(sub_elem) && !w.send(sub_elem).nil?
319
+ end
320
+ end
321
+ end
322
+
323
+ routes&.each do |r|
324
+ xml.rte do
325
+ xml.name r.name
326
+
327
+ r.points.each do |p|
328
+ xml.rtept(lat: p.lat, lon: p.lon) do
329
+ xml.time p.time.xmlschema unless p.time.nil?
330
+ xml.ele p.elevation unless p.elevation.nil?
327
331
  end
328
- }
329
- end unless routes.nil?
330
- }
332
+ end
333
+ end
334
+ end
335
+ end
331
336
  end
332
-
333
- return doc
337
+ # rubocop:enable Metrics/BlockLength
334
338
  end
335
339
 
336
340
  # Calculates and sets the duration attribute by subtracting the time on
337
341
  # the very first point from the time on the very last point.
338
342
  def calculate_duration
339
343
  @duration = 0
340
- if(@tracks.nil? or @tracks.size.zero? or @tracks[0].segments.nil? or @tracks[0].segments.size.zero?)
344
+ if @tracks.nil? || @tracks.empty? || @tracks[0].segments.nil? || @tracks[0].segments.empty?
341
345
  return @duration
346
+
342
347
  end
348
+
343
349
  @duration = (@tracks[-1].segments[-1].points[-1].time - @tracks.first.segments.first.points.first.time)
344
- rescue
350
+ rescue StandardError
345
351
  @duration = 0
346
352
  end
347
353
  end
@@ -1,30 +1,10 @@
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 will parse the lat/lon and time data from a Magellan track log,
25
5
  # which is a NMEA formatted CSV list of points.
26
6
  class MagellanTrackLog
27
- #PMGNTRK
7
+ # PMGNTRK
28
8
  # This message is to be used to transmit Track information (basically a list of previous position fixes)
29
9
  # which is often displayed on the plotter or map screen of the unit. The first field in this message
30
10
  # is the Latitude, followed by N or S. The next field is the Longitude followed by E or W. The next
@@ -35,98 +15,86 @@ module GPX
35
15
  # of the fix. Note that this field is (and its preceding comma) is only produced by the unit when the
36
16
  # command PMGNCMD,TRACK,2 is given. It is not present when a simple command of PMGNCMD,TRACK is issued.
37
17
 
38
- #NOTE: The Latitude and Longitude Fields are shown as having two decimal
18
+ # NOTE: The Latitude and Longitude Fields are shown as having two decimal
39
19
  # places. As many additional decimal places may be added as long as the total
40
20
  # length of the message does not exceed 82 bytes.
41
21
 
42
22
  # $PMGNTRK,llll.ll,a,yyyyy.yy,a,xxxxx,a,hhmmss.ss,A,c----c,ddmmyy*hh<CR><LF>
43
23
  require 'csv'
44
24
 
45
- LAT = 1
46
- LAT_HEMI = 2
47
- LON = 3
48
- LON_HEMI = 4
49
- ELE = 5
25
+ LAT = 1
26
+ LAT_HEMI = 2
27
+ LON = 3
28
+ LON_HEMI = 4
29
+ ELE = 5
50
30
  ELE_UNITS = 6
51
- TIME = 7
31
+ TIME = 7
52
32
  INVALID_FLAG = 8
53
- DATE = 10
33
+ DATE = 10
54
34
 
55
35
  FEET_TO_METERS = 0.3048
56
36
 
57
37
  class << self
58
-
59
38
  # Takes the name of a magellan file, converts the contents to GPX, and
60
39
  # writes the result to gpx_filename.
61
40
  def convert_to_gpx(magellan_filename, gpx_filename)
62
-
63
41
  segment = Segment.new
64
42
 
65
- CSV.open(magellan_filename, "r").each do |row|
66
- next if(row.size < 10 or row[INVALID_FLAG] == 'V')
43
+ CSV.open(magellan_filename, 'r').each do |row|
44
+ next if (row.size < 10) || (row[INVALID_FLAG] == 'V')
67
45
 
68
- lat_deg = row[LAT][0..1]
69
- lat_min = row[LAT][2...-1]
46
+ lat_deg = row[LAT][0..1]
47
+ lat_min = row[LAT][2...-1]
70
48
  lat_hemi = row[LAT_HEMI]
71
49
 
72
50
  lat = lat_deg.to_f + (lat_min.to_f / 60.0)
73
- lat = (-lat) if(lat_hemi == 'S')
51
+ lat = -lat if lat_hemi == 'S'
74
52
 
75
- lon_deg = row[LON][0..2]
76
- lon_min = row[LON][3..-1]
53
+ lon_deg = row[LON][0..2]
54
+ lon_min = row[LON][3..]
77
55
  lon_hemi = row[LON_HEMI]
78
56
 
79
57
  lon = lon_deg.to_f + (lon_min.to_f / 60.0)
80
- lon = (-lon) if(lon_hemi == 'W')
81
-
58
+ lon = -lon if lon_hemi == 'W'
82
59
 
83
60
  ele = row[ELE]
84
61
  ele_units = row[ELE_UNITS]
85
62
  ele = ele.to_f
86
- if(ele_units == 'F')
87
- ele *= FEET_TO_METERS
88
- end
63
+ ele *= FEET_TO_METERS if ele_units == 'F'
89
64
 
90
- hrs = row[TIME][0..1].to_i
65
+ hrs = row[TIME][0..1].to_i
91
66
  mins = row[TIME][2..3].to_i
92
67
  secs = row[TIME][4..5].to_i
93
- day = row[DATE][0..1].to_i
94
- mon = row[DATE][2..3].to_i
95
- yr = 2000 + row[DATE][4..5].to_i
68
+ day = row[DATE][0..1].to_i
69
+ mon = row[DATE][2..3].to_i
70
+ yr = 2000 + row[DATE][4..5].to_i
96
71
 
97
72
  time = Time.gm(yr, mon, day, hrs, mins, secs)
98
73
 
99
- #must create point
100
- pt = TrackPoint.new(:lat => lat, :lon => lon, :time => time, :elevation => ele)
74
+ # must create point
75
+ pt = TrackPoint.new(lat: lat, lon: lon, time: time, elevation: ele)
101
76
  segment.append_point(pt)
102
-
103
77
  end
104
78
 
105
79
  trk = Track.new
106
80
  trk.append_segment(segment)
107
- gpx_file = GPXFile.new(:tracks => [trk])
81
+ gpx_file = GPXFile.new(tracks: [trk])
108
82
  gpx_file.write(gpx_filename)
109
-
110
83
  end
111
84
 
112
85
  # Tests to see if the given file is a magellan NMEA track log.
113
- def is_magellan_file?(filename)
86
+ def magellan_file?(filename)
114
87
  i = 0
115
- File.open(filename, "r") do |f|
88
+ File.open(filename, 'r') do |f|
116
89
  f.each do |line|
117
- i += 1
118
- if line =~ /^\$PMGNTRK/
119
- return true
120
- elsif line =~ /<\?xml/
121
- return false
122
- elsif(i > 10)
123
- return false
124
- end
90
+ i += 1
91
+ return true if line =~ /^\$PMGNTRK/
92
+ return false if line =~ /<\?xml/
93
+ return false if i > 10
125
94
  end
126
95
  end
127
- return false
96
+ false
128
97
  end
129
98
  end
130
-
131
99
  end
132
100
  end
data/lib/gpx/point.rb CHANGED
@@ -1,46 +1,35 @@
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
  # The base class for all points. Trackpoint and Waypoint both descend from this base class.
25
5
  class Point < Base
26
- D_TO_R = Math::PI/180.0;
27
- attr_accessor :lat, :lon, :time, :elevation, :gpx_file, :speed, :extensions
6
+ D_TO_R = Math::PI / 180.0
7
+ attr_accessor :time, :elevation, :gpx_file, :speed, :extensions
8
+ attr_reader :lat, :lon
28
9
 
29
10
  # When you need to manipulate individual points, you can create a Point
30
11
  # object with a latitude, a longitude, an elevation, and a time. In
31
12
  # addition, you can pass an XML element to this initializer, and the
32
13
  # relevant info will be parsed out.
33
- def initialize(opts = {:lat => 0.0, :lon => 0.0, :elevation => 0.0, :time => Time.now } )
14
+ def initialize(opts = { lat: 0.0, lon: 0.0, elevation: 0.0, time: Time.now })
15
+ super()
34
16
  @gpx_file = opts[:gpx_file]
35
- if (opts[:element])
17
+ if opts[:element]
36
18
  elem = opts[:element]
37
- @lat, @lon = elem["lat"].to_f, elem["lon"].to_f
38
- @latr, @lonr = (D_TO_R * @lat), (D_TO_R * @lon)
39
- #'-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
40
- @time = (Time.xmlschema(elem.at("time").inner_text) rescue nil)
41
- @elevation = elem.at("ele").inner_text.to_f unless elem.at("ele").nil?
42
- @speed = elem.at("speed").inner_text.to_f unless elem.at("speed").nil?
43
- @extensions = elem.at("extensions") unless elem.at("extensions").nil?
19
+ @lat = elem['lat'].to_f
20
+ @lon = elem['lon'].to_f
21
+ @latr = (D_TO_R * @lat)
22
+ @lonr = (D_TO_R * @lon)
23
+ # '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?
24
+ @time = (
25
+ begin
26
+ Time.xmlschema(elem.at('time').inner_text)
27
+ rescue StandardError
28
+ nil
29
+ end)
30
+ @elevation = elem.at('ele').inner_text.to_f unless elem.at('ele').nil?
31
+ @speed = elem.at('speed').inner_text.to_f unless elem.at('speed').nil?
32
+ @extensions = elem.at('extensions') unless elem.at('extensions').nil?
44
33
  else
45
34
  @lat = opts[:lat]
46
35
  @lon = opts[:lon]
@@ -49,10 +38,8 @@ module GPX
49
38
  @speed = opts[:speed]
50
39
  @extensions = opts[:extensions]
51
40
  end
52
-
53
41
  end
54
42
 
55
-
56
43
  # Returns the latitude and longitude (in that order), separated by the
57
44
  # given delimeter. This is useful for passing a point into another API
58
45
  # (i.e. the Google Maps javascript API).