gpx 0.9.0 → 1.0.0

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.
@@ -1,25 +1,3 @@
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
- #++
23
1
  module GPX
24
2
  # In GPX, a single Track can hold multiple Segments, each of which hold
25
3
  # multiple points (in this library, those points are instances of
@@ -38,24 +16,36 @@ module GPX
38
16
  @segments = []
39
17
  @points = []
40
18
  reset_meta_data
41
- if(opts[:element])
42
- trk_element = opts[:element]
43
- @name = (trk_element.at("name").inner_text rescue "")
44
- @comment = (trk_element.at('cmt').inner_text rescue '')
45
- @description = (trk_element.at('desc').inner_text rescue '')
46
- trk_element.search("trkseg").each do |seg_element|
47
- seg = Segment.new(:element => seg_element, :track => self, :gpx_file => @gpx_file)
48
- update_meta_data(seg)
49
- @segments << seg
50
- end
19
+
20
+ return unless opts[:element]
21
+
22
+ trk_element = opts[:element]
23
+ @name = (begin
24
+ trk_element.at('name').inner_text
25
+ rescue StandardError
26
+ ''
27
+ end)
28
+ @comment = (begin
29
+ trk_element.at('cmt').inner_text
30
+ rescue StandardError
31
+ ''
32
+ end)
33
+ @description = (begin
34
+ trk_element.at('desc').inner_text
35
+ rescue StandardError
36
+ ''
37
+ end)
38
+ trk_element.search('trkseg').each do |seg_element|
39
+ seg = Segment.new(element: seg_element, track: self, gpx_file: @gpx_file)
40
+ append_segment(seg)
51
41
  end
52
42
  end
53
43
 
54
44
  # Append a segment to this track, updating its meta data along the way.
55
45
  def append_segment(seg)
46
+ return if seg.points.empty?
56
47
  update_meta_data(seg)
57
48
  @segments << seg
58
- @points.concat(seg.points) unless seg.nil?
59
49
  end
60
50
 
61
51
  # Returns true if the given time occurs within any of the segments of this track.
@@ -63,7 +53,7 @@ module GPX
63
53
  segments.each do |seg|
64
54
  return true if seg.contains_time?(time)
65
55
  end
66
- return false
56
+ false
67
57
  end
68
58
 
69
59
  # Finds the closest point (to "time") within this track. Useful for
@@ -82,7 +72,7 @@ module GPX
82
72
  seg.crop(area)
83
73
  update_meta_data(seg) unless seg.empty?
84
74
  end
85
- segments.delete_if { |seg| seg.empty? }
75
+ segments.delete_if(&:empty?)
86
76
  end
87
77
 
88
78
  # Deletes all points within a given area and updates the meta data.
@@ -92,13 +82,13 @@ module GPX
92
82
  seg.delete_area(area)
93
83
  update_meta_data(seg) unless seg.empty?
94
84
  end
95
- segments.delete_if { |seg| seg.empty? }
85
+ segments.delete_if(&:empty?)
96
86
  end
97
87
 
98
88
  # Returns true if this track has no points in it. This should return
99
89
  # true even when the track has empty segments.
100
90
  def empty?
101
- (points.nil? or points.size.zero?)
91
+ (points.nil? || points.size.zero?)
102
92
  end
103
93
 
104
94
  # Prints out a friendly summary of this track (sans points). Useful for
@@ -115,7 +105,7 @@ module GPX
115
105
  result << "\tMoving duration: #{moving_duration} km\n"
116
106
  result << "\tLowest Point: #{lowest_point.elevation} \n"
117
107
  result << "\tHighest Point: #{highest_point.elevation}\n "
118
- result << "\tBounds: #{bounds.to_s}"
108
+ result << "\tBounds: #{bounds}"
119
109
  result
120
110
  end
121
111
 
@@ -125,12 +115,12 @@ module GPX
125
115
  @distance += seg.distance
126
116
  end
127
117
  end
128
-
118
+
129
119
  protected
130
120
 
131
121
  def update_meta_data(seg)
132
- @lowest_point = seg.lowest_point if(@lowest_point.nil? or seg.lowest_point.elevation < @lowest_point.elevation)
133
- @highest_point = seg.highest_point if(@highest_point.nil? or seg.highest_point.elevation > @highest_point.elevation)
122
+ @lowest_point = seg.lowest_point if @lowest_point.nil? || (seg.lowest_point.elevation < @lowest_point.elevation)
123
+ @highest_point = seg.highest_point if @highest_point.nil? || (seg.highest_point.elevation > @highest_point.elevation)
134
124
  @bounds.add(seg.bounds)
135
125
  @distance += seg.distance
136
126
  @moving_duration += seg.duration
@@ -145,6 +135,5 @@ module GPX
145
135
  @moving_duration = 0.0
146
136
  @points = []
147
137
  end
148
-
149
138
  end
150
139
  end
@@ -0,0 +1,30 @@
1
+ module GPX
2
+ # Basically the same as a point, the TrackPoint class is supposed to
3
+ # represent the points that are children of Segment elements. So, the only
4
+ # real difference is that TrackPoints hold a reference to their parent
5
+ # Segments.
6
+ class TrackPoint < Point
7
+ RADIUS = 6371 # earth's mean radius in km
8
+
9
+ attr_accessor :segment
10
+
11
+ def initialize(opts = {})
12
+ super(opts)
13
+ @segment = opts[:segment]
14
+ end
15
+
16
+ # Units are in km
17
+ def haversine_distance_from(p2)
18
+ d_lat = p2.latr - latr
19
+ d_lon = p2.lonr - lonr
20
+ a = Math.sin(d_lat / 2) * Math.sin(d_lat / 2) + Math.cos(latr) * Math.cos(p2.latr) * Math.sin(d_lon / 2) * Math.sin(d_lon / 2)
21
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
22
+ RADIUS * c
23
+ end
24
+
25
+ # Units are in km
26
+ def law_of_cosines_distance_from(p2)
27
+ Math.acos(Math.sin(latr) * Math.sin(p2.latr) + Math.cos(latr) * Math.cos(p2.latr) * Math.cos(p2.lonr - lonr)) * RADIUS
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,3 @@
1
1
  module GPX
2
- VERSION = "0.9.0"
2
+ VERSION = "1.0.0".freeze
3
3
  end
@@ -1,56 +1,29 @@
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
- #++
23
1
  module GPX
24
2
  # This class supports the concept of a waypoint. Beware that this class has
25
3
  # not seen much use yet, since WalkingBoss does not use waypoints right now.
26
4
  class Waypoint < Point
27
-
28
- SUB_ELEMENTS = %w{ele magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid extensions}
5
+ SUB_ELEMENTS = %w[ele magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid extensions].freeze
29
6
 
30
7
  attr_reader :gpx_file
31
8
  SUB_ELEMENTS.each { |sub_el| attr_accessor sub_el.to_sym }
32
9
 
33
10
  # Not implemented
34
- def crop(area)
35
- end
11
+ def crop(area); end
36
12
 
37
13
  # Not implemented
38
- def delete_area(area)
39
- end
14
+ def delete_area(area); end
40
15
 
41
16
  # Initializes a waypoint from a XML::Node.
42
17
  def initialize(opts = {})
43
- if(opts[:element] and opts[:gpx_file])
18
+ if opts[:element] && opts[:gpx_file]
44
19
  wpt_elem = opts[:element]
45
20
  @gpx_file = opts[:gpx_file]
46
- super(:element => wpt_elem, :gpx_file => @gpx_file)
21
+ super(element: wpt_elem, gpx_file: @gpx_file)
47
22
  instantiate_with_text_elements(wpt_elem, SUB_ELEMENTS)
48
23
  else
49
24
  opts.each do |key, value|
50
25
  assignment_method = "#{key}="
51
- if self.respond_to?(assignment_method)
52
- self.send(assignment_method, value)
53
- end
26
+ send(assignment_method, value) if respond_to?(assignment_method)
54
27
  end
55
28
  end
56
29
  end
@@ -65,7 +38,7 @@ module GPX
65
38
  result << "\tElevation: #{elevation}\n "
66
39
  result << "\tTime: #{time}\n"
67
40
  SUB_ELEMENTS.each do |sub_element_attribute|
68
- val = self.send(sub_element_attribute)
41
+ val = send(sub_element_attribute)
69
42
  result << "\t#{sub_element_attribute}: #{val}\n" unless val.nil?
70
43
  end
71
44
  result
@@ -2,11 +2,10 @@ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
4
  class GPX10Test < Minitest::Test
5
- GPX_FILE = File.join(File.dirname(__FILE__), "gpx_files/gpx10.gpx")
6
-
7
- def test_read
8
- # make sure we can read a GPX 1.0 file
9
- @gpx_file = GPX::GPXFile.new(:gpx_file => GPX_FILE)
10
- end
5
+ GPX_FILE = File.join(File.dirname(__FILE__), 'gpx_files/gpx10.gpx')
11
6
 
7
+ def test_read
8
+ # make sure we can read a GPX 1.0 file
9
+ @gpx_file = GPX::GPXFile.new(gpx_file: GPX_FILE)
10
+ end
12
11
  end
@@ -2,18 +2,18 @@ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
4
  class GPXFileTest < Minitest::Test
5
-
6
- ONE_TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/one_track.gpx")
7
- WITH_OR_WITHOUT_ELEV_FILE = File.join(File.dirname(__FILE__), "gpx_files/with_or_without_elev.gpx")
8
- BIG_FILE = File.join(File.dirname(__FILE__), "gpx_files/big.gpx")
5
+ ONE_TRACK_FILE = File.join(File.dirname(__FILE__), 'gpx_files/one_track.gpx')
6
+ WITH_OR_WITHOUT_ELEV_FILE = File.join(File.dirname(__FILE__), 'gpx_files/with_or_without_elev.gpx')
7
+ WITH_EMPTY_TRACKS = File.join(File.dirname(__FILE__), 'gpx_files/with_empty_tracks.gpx')
8
+ BIG_FILE = File.join(File.dirname(__FILE__), 'gpx_files/big.gpx')
9
9
 
10
10
  def test_load_data_from_string
11
- gpx_file = GPX::GPXFile.new(:gpx_data => open(ONE_TRACK_FILE).read)
11
+ gpx_file = GPX::GPXFile.new(gpx_data: File.open(ONE_TRACK_FILE).read)
12
12
  assert_equal(1, gpx_file.tracks.size)
13
13
  assert_equal(8, gpx_file.tracks.first.segments.size)
14
- assert_equal("ACTIVE LOG", gpx_file.tracks.first.name)
15
- assert_equal("active_log.gpx", gpx_file.name)
16
- assert_equal("2006-04-08T16:44:28Z", gpx_file.time.xmlschema)
14
+ assert_equal('ACTIVE LOG', gpx_file.tracks.first.name)
15
+ assert_equal('active_log.gpx', gpx_file.name)
16
+ assert_equal('2006-04-08T16:44:28Z', gpx_file.time.xmlschema)
17
17
  assert_equal(38.681488, gpx_file.bounds.min_lat)
18
18
  assert_equal(-109.606948, gpx_file.bounds.min_lon)
19
19
  assert_equal(38.791759, gpx_file.bounds.max_lat)
@@ -21,45 +21,55 @@ class GPXFileTest < Minitest::Test
21
21
  assert_equal('description of my GPX file with special char like &, <, >', gpx_file.description)
22
22
  assert_equal('description of my GPX file with special char like &, <, >', gpx_file.description)
23
23
  assert_equal(3.0724966849262554, gpx_file.distance)
24
- assert_equal(15237.0, gpx_file.duration)
24
+ assert_equal(15_237.0, gpx_file.duration)
25
25
  assert_equal(3036.0, gpx_file.moving_duration)
26
26
  assert_equal(3.6432767014935834, gpx_file.average_speed)
27
27
  end
28
28
 
29
29
  def test_load_data_from_file
30
- gpx_file = GPX::GPXFile.new(:gpx_file => ONE_TRACK_FILE)
30
+ gpx_file = GPX::GPXFile.new(gpx_file: ONE_TRACK_FILE)
31
31
  assert_equal(1, gpx_file.tracks.size)
32
32
  assert_equal(8, gpx_file.tracks.first.segments.size)
33
- assert_equal("ACTIVE LOG", gpx_file.tracks.first.name)
34
- assert_equal("active_log.gpx", gpx_file.name)
35
- assert_equal("2006-04-08T16:44:28Z", gpx_file.time.xmlschema)
33
+ assert_equal('ACTIVE LOG', gpx_file.tracks.first.name)
34
+ assert_equal('active_log.gpx', gpx_file.name)
35
+ assert_equal('2006-04-08T16:44:28Z', gpx_file.time.xmlschema)
36
36
  assert_equal(38.681488, gpx_file.bounds.min_lat)
37
37
  assert_equal(-109.606948, gpx_file.bounds.min_lon)
38
38
  assert_equal(38.791759, gpx_file.bounds.max_lat)
39
39
  assert_equal(-109.447045, gpx_file.bounds.max_lon)
40
40
  assert_equal('description of my GPX file with special char like &, <, >', gpx_file.description)
41
41
  assert_equal(3.0724966849262554, gpx_file.distance)
42
- assert_equal(15237.0, gpx_file.duration)
42
+ assert_equal(15_237.0, gpx_file.duration)
43
43
  assert_equal(3036.0, gpx_file.moving_duration)
44
44
  assert_equal(3.6432767014935834, gpx_file.average_speed)
45
45
  end
46
46
 
47
47
  def test_big_file
48
- gpx_file = GPX::GPXFile.new(:gpx_file => BIG_FILE)
48
+ gpx_file = GPX::GPXFile.new(gpx_file: BIG_FILE)
49
49
  assert_equal(1, gpx_file.tracks.size)
50
50
  assert_equal(7968, gpx_file.tracks.first.points.size)
51
- assert_equal(105508.0, gpx_file.duration)
52
- assert_equal(57645.0, gpx_file.moving_duration)
51
+ assert_equal(105_508.0, gpx_file.duration)
52
+ assert_equal(57_645.0, gpx_file.moving_duration)
53
53
  assert_in_delta(99.60738958686505, gpx_file.average_speed, 1e-13)
54
54
  end
55
55
 
56
56
  def test_with_or_with_elev
57
- gpx_file = GPX::GPXFile.new(:gpx_file => WITH_OR_WITHOUT_ELEV_FILE)
57
+ gpx_file = GPX::GPXFile.new(gpx_file: WITH_OR_WITHOUT_ELEV_FILE)
58
58
  assert_equal(2, gpx_file.tracks.size)
59
59
  assert_equal(0, gpx_file.duration)
60
60
  assert_equal(0, gpx_file.moving_duration)
61
61
  assert(gpx_file.average_speed.nan?)
62
- #assert_equal(7968, gpx_file.tracks.first.points.size)
62
+ # assert_equal(7968, gpx_file.tracks.first.points.size)
63
63
  end
64
64
 
65
+ def test_with_empty_tracks
66
+ gpx_file = GPX::GPXFile.new(gpx_file: WITH_EMPTY_TRACKS)
67
+ # is read correctly
68
+ assert_equal(1, gpx_file.tracks.size)
69
+ # and ignores empty segments
70
+ assert_equal(1, gpx_file.tracks.first.segments.size)
71
+ assert_equal(21.0, gpx_file.duration)
72
+ assert_equal(21.0, gpx_file.moving_duration)
73
+ assert_equal(6.674040636626879, gpx_file.average_speed)
74
+ end
65
75
  end
@@ -0,0 +1,72 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="Open GPX Tracker for iOS">
3
+ <wpt lat="51.185170" lon="4.662453">
4
+ <time>2018-01-16T11:35:01Z</time>
5
+ <name>12:35:01</name>
6
+ <desc>16 jan. 2018 12:35:01</desc>
7
+ </wpt>
8
+ <trk>
9
+ <trkseg>
10
+ <trkpt lat="51.183019" lon="4.664619">
11
+ <ele>0.751247</ele>
12
+ <time>2018-01-16T11:06:00Z</time>
13
+ </trkpt>
14
+ <trkpt lat="51.183037" lon="4.664587">
15
+ <ele>0.622036</ele>
16
+ <time>2018-01-16T11:06:02Z</time>
17
+ </trkpt>
18
+ <trkpt lat="51.183053" lon="4.664553">
19
+ <ele>0.042019</ele>
20
+ <time>2018-01-16T11:06:04Z</time>
21
+ </trkpt>
22
+ <trkpt lat="51.183073" lon="4.664515">
23
+ <ele>0.177456</ele>
24
+ <time>2018-01-16T11:06:06Z</time>
25
+ </trkpt>
26
+ <trkpt lat="51.183095" lon="4.664480">
27
+ <ele>0.564846</ele>
28
+ <time>2018-01-16T11:06:08Z</time>
29
+ </trkpt>
30
+ <trkpt lat="51.183115" lon="4.664446">
31
+ <ele>0.941067</ele>
32
+ <time>2018-01-16T11:06:10Z</time>
33
+ </trkpt>
34
+ <trkpt lat="51.183143" lon="4.664401">
35
+ <ele>1.960781</ele>
36
+ <time>2018-01-16T11:06:12Z</time>
37
+ </trkpt>
38
+ <trkpt lat="51.183160" lon="4.664372">
39
+ <ele>2.115200</ele>
40
+ <time>2018-01-16T11:06:13Z</time>
41
+ </trkpt>
42
+ <trkpt lat="51.183176" lon="4.664342">
43
+ <ele>2.191250</ele>
44
+ <time>2018-01-16T11:06:14Z</time>
45
+ </trkpt>
46
+ <trkpt lat="51.183192" lon="4.664315">
47
+ <ele>2.492947</ele>
48
+ <time>2018-01-16T11:06:15Z</time>
49
+ </trkpt>
50
+ <trkpt lat="51.183205" lon="4.664291">
51
+ <ele>2.459927</ele>
52
+ <time>2018-01-16T11:06:16Z</time>
53
+ </trkpt>
54
+ <trkpt lat="51.183217" lon="4.664270">
55
+ <ele>2.226833</ele>
56
+ <time>2018-01-16T11:06:17Z</time>
57
+ </trkpt>
58
+ <trkpt lat="51.183240" lon="4.664233">
59
+ <ele>2.175198</ele>
60
+ <time>2018-01-16T11:06:19Z</time>
61
+ </trkpt>
62
+ <trkpt lat="51.183255" lon="4.664207">
63
+ <ele>2.643887</ele>
64
+ <time>2018-01-16T11:06:21Z</time>
65
+ </trkpt>
66
+ </trkseg>
67
+ <trkseg>
68
+ </trkseg>
69
+ <trkseg>
70
+ </trkseg>
71
+ </trk>
72
+ </gpx>
@@ -2,17 +2,16 @@ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
4
  class MagellanTest < Minitest::Test
5
- MAGELLAN_TRACK_LOG = File.join(File.dirname(__FILE__), "gpx_files/magellan_track.log")
6
- GPX_FILE = File.join(File.dirname(__FILE__), "gpx_files/one_segment.gpx")
5
+ MAGELLAN_TRACK_LOG = File.join(File.dirname(__FILE__), 'gpx_files/magellan_track.log')
6
+ GPX_FILE = File.join(File.dirname(__FILE__), 'gpx_files/one_segment.gpx')
7
7
 
8
- def test_convert
9
- GPX::MagellanTrackLog.convert_to_gpx(MAGELLAN_TRACK_LOG, "/tmp/gpx_from_magellan.gpx")
10
- @gpx_file = GPX::GPXFile.new(:gpx_file => "/tmp/gpx_from_magellan.gpx")
11
- end
12
-
13
- def test_file_type
14
- assert(GPX::MagellanTrackLog::is_magellan_file?(MAGELLAN_TRACK_LOG))
15
- assert(!GPX::MagellanTrackLog::is_magellan_file?(GPX_FILE))
16
- end
8
+ def test_convert
9
+ GPX::MagellanTrackLog.convert_to_gpx(MAGELLAN_TRACK_LOG, '/tmp/gpx_from_magellan.gpx')
10
+ @gpx_file = GPX::GPXFile.new(gpx_file: '/tmp/gpx_from_magellan.gpx')
11
+ end
17
12
 
13
+ def test_file_type
14
+ assert(GPX::MagellanTrackLog.magellan_file?(MAGELLAN_TRACK_LOG))
15
+ assert(!GPX::MagellanTrackLog.magellan_file?(GPX_FILE))
16
+ end
18
17
  end