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/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).