gpx_ruby 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d28ab6aad3c8f49b2a1623c39a58a54a550e2fc
4
+ data.tar.gz: 9e87c82457414596fdad17d515489e7cf4a01375
5
+ SHA512:
6
+ metadata.gz: f1e26c42d0a11f5e6598f0f486b189ebdb37bf0af645dea7320ff7a736150a877123bf02d2996d7ffa885978b2e203406e870efcc197f8b397c05135b303b328
7
+ data.tar.gz: 241a076a866fefeb6e476ef24aa26e40f44941a82cdc6ad6041e1a7fe613d37cae92648743194288fdcb017c9da6c0ce8f91a118bb45ab07b0276d84facc9281
@@ -0,0 +1,7 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |task|
4
+ task.libs = ['.', 'lib', 'test']
5
+ task.test_files = FileList['test/test_*.rb']
6
+ task.verbose = true
7
+ end
@@ -0,0 +1,24 @@
1
+ module GpxRuby
2
+
3
+ require 'nokogiri'
4
+ require 'time'
5
+ require 'tempfile'
6
+ require 'gpx_ruby/gpx/point'
7
+ require 'gpx_ruby/gpx/track'
8
+ require 'gpx_ruby/gpx/parsers/track_parser'
9
+ require 'gpx_ruby/gpx/document'
10
+ require 'gpx_ruby/gpx/parser'
11
+
12
+ class << self
13
+
14
+ def File(input)
15
+ Gpx::Parser.new(input).parse
16
+ end
17
+
18
+ def XML(xml_string)
19
+ Gpx::Parser.new(xml: xml_string).parse
20
+ end
21
+
22
+ end
23
+
24
+ end
@@ -0,0 +1,19 @@
1
+ module GpxRuby
2
+
3
+ module Gpx
4
+
5
+ class Document
6
+
7
+ attr_accessor :version, :creator, :tracks
8
+
9
+ def initialize(a_hash)
10
+ @creator = a_hash[:creator]
11
+ @version = a_hash[:version]
12
+ @tracks = a_hash[:tracks]
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,54 @@
1
+ module GpxRuby
2
+
3
+ module Gpx
4
+
5
+ class Parser
6
+
7
+ def initialize(input)
8
+ @xml = case input
9
+ when File
10
+ input.read
11
+ when Tempfile
12
+ input.read
13
+ when String
14
+ ::File.open(input, 'r') { |f| f.read }
15
+ when Hash
16
+ if input[:file_path] && input[:file_path].is_a?(String)
17
+ ::File.open(input[:file_path], 'r') { |f| f.read }
18
+ end
19
+ if input[:xml] && input[:xml].is_a?(String)
20
+ input[:xml]
21
+ elsif input[:file] && (input[:file].is_a?(File) || input[:file].is_a?(Tempfile))
22
+ input[:file].read
23
+ else
24
+ raise 'Error: invalid input!'
25
+ end
26
+ else
27
+ raise 'Error: invalid input!'
28
+ end
29
+ end
30
+
31
+
32
+ def parse
33
+ doc = Nokogiri::XML(@xml)
34
+ @gpx_node = doc.at_xpath('//xmlns:gpx')
35
+
36
+ properties = {
37
+ creator: @gpx_node.at_xpath('@creator').value,
38
+ version: @gpx_node.at_xpath('@version').value.to_f,
39
+ tracks: []
40
+ }
41
+
42
+ # parse track elements
43
+ tracks = Parsers::TrackParser.new(@gpx_node).parse
44
+ properties = properties.merge(tracks: tracks)
45
+
46
+ Document.new properties
47
+ end
48
+
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,121 @@
1
+ module GpxRuby
2
+
3
+ module Gpx
4
+
5
+ module Parsers
6
+
7
+ class TrackParser
8
+
9
+ def initialize(gpx_node)
10
+ @gpx_node = gpx_node
11
+ end
12
+
13
+ def parse
14
+ tracks = []
15
+ if track_nodes = @gpx_node.xpath('./xmlns:trk')
16
+ # iterate over all tracks
17
+ track_nodes.each do |trk|
18
+ trk_segments = trk.xpath('./xmlns:trkseg')
19
+
20
+ # map track segments for this track
21
+ track_segments = trk_segments.map do |trkseg|
22
+
23
+ # map all track points for this track segment
24
+ track_points = trkseg.xpath('./xmlns:trkpt').map do |trkpt|
25
+
26
+ lat = trkpt.at_xpath('./@lat').value.to_f
27
+ lon = trkpt.at_xpath('./@lon').value.to_f
28
+
29
+ time = begin
30
+ DateTime.xmlschema(trkpt.at_xpath('./xmlns:time').text).to_time
31
+ rescue
32
+ nil
33
+ end
34
+
35
+ elevation = begin
36
+ trkpt.at_xpath('./xmlns:ele').text.to_f
37
+ rescue
38
+ nil
39
+ end
40
+
41
+ Track::Point.new lat: lat, lon: lon, time: time, elevation: elevation
42
+ end
43
+
44
+ Track::Segment.new track_points
45
+ end
46
+
47
+ trk_name = begin
48
+ trk.at_xpath('./xmlns:name').text
49
+ rescue
50
+ nil
51
+ end
52
+
53
+ trk_cmt = begin
54
+ trk.at_xpath('./xmlns:cmt').text
55
+ rescue
56
+ nil
57
+ end
58
+
59
+ trk_src = begin
60
+ trk.at_xpath('./xmlns:src').text
61
+ rescue
62
+ nil
63
+ end
64
+
65
+ trk_desc = begin
66
+ trk.at_xpath('./xmlns:desc').text
67
+ rescue
68
+ nil
69
+ end
70
+
71
+ trk_number = begin
72
+ trk.at_xpath('./xmlns:number').text
73
+ rescue
74
+ nil
75
+ end
76
+
77
+ trk_url = begin
78
+ trk.at_xpath('./xmlns:url').text
79
+ rescue
80
+ nil
81
+ end
82
+
83
+ trk_url_name = begin
84
+ trk.at_xpath('./xmlns:urlname').text
85
+ rescue
86
+ nil
87
+ end
88
+
89
+ trk_time = begin
90
+ DateTime.xmlschema(trk.at_xpath('./xmlns:time').text).to_time
91
+ rescue
92
+ nil
93
+ end
94
+
95
+ track_hash = {
96
+ segments: track_segments,
97
+ name: trk_name,
98
+ time: trk_time,
99
+ source: trk_src,
100
+ description: trk_desc,
101
+ number: trk_number,
102
+ comment: trk_cmt,
103
+ url: trk_url,
104
+ url_name: trk_url_name
105
+ }
106
+
107
+ tracks << Track.new(track_hash)
108
+ end
109
+
110
+ end
111
+
112
+ tracks
113
+ end
114
+
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,78 @@
1
+ module GpxRuby
2
+
3
+ module Gpx
4
+
5
+ class Point
6
+
7
+ attr_accessor :lat, :lon
8
+
9
+ RADIUS_KM = 6371
10
+
11
+ def initialize(a_hash)
12
+ @lat = a_hash[:lat]
13
+ @lon = a_hash[:lon]
14
+ end
15
+
16
+
17
+ def [](index)
18
+ case index
19
+ when 0
20
+ lat
21
+ when 1
22
+ lon
23
+ else
24
+ raise 'Invalid index!'
25
+ end
26
+ end
27
+
28
+ def +(a_point)
29
+ nlat = lat + a_point.lat
30
+ nlon = lon + a_point.lon
31
+ Point.new lat: nlat, lon: nlon
32
+ end
33
+
34
+ def /(a_scalar)
35
+ nlat = lat / a_scalar.to_f
36
+ nlon = lon / a_scalar.to_f
37
+ Point.new lat: nlat, lon: nlon
38
+ end
39
+
40
+ def to_s
41
+ "lat: #{lat}, lon: #{lon}"
42
+ end
43
+
44
+ def to_a
45
+ [lat, lon]
46
+ end
47
+
48
+ def to_hash
49
+ { lat: lat, lon: lon }
50
+ end
51
+
52
+ alias_method :to_h, :to_hash
53
+
54
+
55
+ def distance(a_point)
56
+ p_point = self
57
+ rad_per_deg, radius_meter = Math::PI/180, RADIUS_KM * 1000
58
+
59
+ dlon_rad = (a_point.lon-p_point.lon) * rad_per_deg
60
+ dlat_rad = (a_point.lat-p_point.lat) * rad_per_deg
61
+
62
+ lat1_rad = p_point.lat * rad_per_deg
63
+ lat2_rad = a_point.lat * rad_per_deg
64
+
65
+ p_point = Math.sin(dlat_rad/2)**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * Math.sin(dlon_rad/2)**2
66
+ c = 2 * Math::atan2(Math::sqrt(p_point), Math::sqrt(1-p_point))
67
+
68
+ radius_meter * c
69
+ end
70
+
71
+
72
+ end
73
+
74
+ end
75
+
76
+
77
+
78
+ end
@@ -0,0 +1,100 @@
1
+ module GpxRuby
2
+
3
+ module Gpx
4
+
5
+ class Track
6
+
7
+ attr_accessor :segments, :name, :time,
8
+ :description, :source, :comment,
9
+ :number, :url, :url_name
10
+
11
+ def initialize(a_hash)
12
+ @name = a_hash[:name]
13
+ @time = a_hash[:time]
14
+ @segments = a_hash[:segments] || []
15
+ @number = a_hash[:number]
16
+ @source = a_hash[:source]
17
+ @url = a_hash[:url]
18
+ @url_name = a_hash[:url_name]
19
+ @comment = a_hash[:comment]
20
+ @description = a_hash[:description]
21
+ end
22
+
23
+ def points
24
+ @points ||= @segments.map {|segment| segment.points }.reduce(:+)
25
+ end
26
+
27
+ def distance
28
+ @distance ||= @segments.map {|segment| segment.distance }.reduce(:+)
29
+ end
30
+
31
+ def total_time
32
+ start_time = @segments.first.points.first.time
33
+ end_time = @segments.last.points.last.time
34
+ end_time - start_time rescue 0
35
+ end
36
+
37
+ def center_of_gravity
38
+ track_points = points
39
+ len = track_points.size
40
+ center = track_points.reduce(:+)
41
+ center / len
42
+ end
43
+
44
+
45
+ class Point < Gpx::Point
46
+
47
+ attr_accessor :lat, :lon, :time, :elevation
48
+
49
+ def initialize(params)
50
+ @lat = params[:lat]
51
+ @lon = params[:lon]
52
+ @time = params[:time]
53
+ @elevation = params[:elevation]
54
+ end
55
+
56
+ def to_a
57
+ [@lat,@lon]
58
+ end
59
+
60
+ end
61
+
62
+
63
+ class Segment
64
+
65
+ attr_accessor :points
66
+
67
+ def initialize(points)
68
+ @points = points || []
69
+ end
70
+
71
+ def distance
72
+ if @distance
73
+ @distance
74
+ else
75
+ zipped_points = @points[0...-1].zip(@points[1..-1])
76
+ distances = zipped_points.map {|(p,q)| p.distance(q) }
77
+ @distance = distances.reduce(:+)
78
+ end
79
+ end
80
+
81
+ def total_time
82
+ start_time, end_time = @points.first.time, @points.last.time
83
+ end_time - start_time rescue 0
84
+ end
85
+
86
+ def center_of_gravity
87
+ track_points = points
88
+ len = track_points.size
89
+ center = track_points.reduce(:+)
90
+ center / len
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+
99
+
100
+ end
@@ -0,0 +1,3 @@
1
+ module GpxRuby
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,167 @@
1
+ require 'test/test_helper'
2
+
3
+ class TestGpxParser < MiniTest::Test
4
+
5
+ def setup
6
+ @gpx_parser = GpxRuby::Gpx::Parser.new './test/run_keeper.gpx'
7
+ @gpx_file = @gpx_parser.parse
8
+ end
9
+
10
+ def test_should_read_gpx_file
11
+ gpx_parser = GpxRuby::Gpx::Parser.new File.open('./test/run_keeper.gpx','r')
12
+ gpx_file = gpx_parser.parse
13
+ assert gpx_file.creator
14
+ assert gpx_file.version
15
+ end
16
+
17
+
18
+ def test_should_read_gpx_temp_file
19
+ xml = File.open('./test/run_keeper.gpx','r') { |f| f.read }
20
+ tmp_file = Tempfile.open('blah.gpx')
21
+ tmp_file.write xml
22
+ tmp_file.rewind
23
+ gpx_parser = GpxRuby::Gpx::Parser.new tmp_file
24
+ gpx_file = gpx_parser.parse
25
+ assert gpx_file.creator
26
+ assert gpx_file.version
27
+ end
28
+
29
+
30
+ def test_should_read_file_path
31
+ gpx_parser = GpxRuby::Gpx::Parser.new './test/run_keeper.gpx'
32
+ gpx_file = gpx_parser.parse
33
+ assert gpx_file.creator
34
+ assert gpx_file.version
35
+ end
36
+
37
+
38
+ def test_should_read_gpx_xml
39
+ xml = File.open('./test/run_keeper.gpx') do |f|
40
+ f.read
41
+ end
42
+ gpx_parser = GpxRuby::Gpx::Parser.new xml: xml
43
+ gpx_file = gpx_parser.parse
44
+ assert gpx_file.creator
45
+ assert gpx_file.version
46
+ end
47
+
48
+
49
+ def test_should_read_gpx_xml_using_a_shortcut
50
+ xml = File.open('./test/run_keeper.gpx') do |f|
51
+ f.read
52
+ end
53
+ gpx_file = GpxRuby::XML(xml)
54
+ assert gpx_file.creator
55
+ assert gpx_file.version
56
+ end
57
+
58
+
59
+ def test_should_read_gpx_file_using_a_shortcut
60
+ gpx_file = GpxRuby::File './test/run_keeper.gpx'
61
+ assert gpx_file.creator
62
+ assert gpx_file.version
63
+ end
64
+
65
+
66
+ def test_should_compute_center_of_gravity
67
+ gpx_parser = GpxRuby::Gpx::Parser.new './test/run_keeper.gpx'
68
+ gpx_file = gpx_parser.parse
69
+ track = gpx_file.tracks.first
70
+ pp 'center of gravity: %s' % track.center_of_gravity
71
+ end
72
+
73
+
74
+ def test_should_read_fell_loops
75
+ gpx_file = GpxRuby::File './test/gpx_samples/fells_loop.gpx'
76
+ assert gpx_file.creator
77
+ assert gpx_file.version
78
+ track = gpx_file.tracks.first
79
+ assert track.nil?
80
+ gpx_file.tracks.each do |track|
81
+ assert track.name || track.name.nil?
82
+ assert track.time || track.time.nil?
83
+ assert track.distance
84
+ end
85
+ end
86
+
87
+
88
+ def test_should_read_boise_front
89
+ gpx_file = GpxRuby.File './test/gpx_samples/BoiseFront.gpx'
90
+ assert gpx_file.creator
91
+ assert gpx_file.version
92
+ track = gpx_file.tracks.first
93
+ assert track.nil? == false
94
+ assert track.total_time == 0
95
+ gpx_file.tracks.each do |track|
96
+ assert track.name || track.name.nil?
97
+ assert track.time || track.time.nil?
98
+ assert track.distance
99
+ end
100
+ end
101
+
102
+
103
+ def test_should_read_track
104
+ track = @gpx_file.tracks.first
105
+ assert track
106
+ assert track.is_a? GpxRuby::Gpx::Track
107
+ end
108
+
109
+
110
+ def test_should_read_track_segments
111
+ track = @gpx_file.tracks.first
112
+ track_segments = track.segments
113
+ assert track_segments.size == 4
114
+ end
115
+
116
+
117
+ def test_should_read_track_segment_points
118
+ track = @gpx_file.tracks.first
119
+ track_segment = track.segments.last
120
+ assert track_segment.points.size > 0
121
+ track_points = track_segment.points
122
+ track_points.each do |track_point|
123
+ assert track_point.lat
124
+ assert track_point.lon
125
+ assert track_point.elevation
126
+ end
127
+
128
+ end
129
+
130
+
131
+ def test_should_read_track_points
132
+ track = @gpx_file.tracks.first
133
+ assert track
134
+ assert track.points.size > 0
135
+ track_points = track.points
136
+ track_points.each do |track_point|
137
+ assert track_point.lat
138
+ assert track_point.lon
139
+ assert track_point.elevation
140
+ end
141
+ end
142
+
143
+
144
+ def test_should_compute_total_track_segment_distance
145
+ track = @gpx_file.tracks.first
146
+ puts track.segments.map {|segment| segment.distance }
147
+ end
148
+
149
+
150
+ def test_should_compute_total_track_distance
151
+ track = @gpx_file.tracks.first
152
+ puts track.distance
153
+ end
154
+
155
+
156
+ def test_should_compute_total_track_time
157
+ track = @gpx_file.tracks.first
158
+ puts track.total_time
159
+ end
160
+
161
+
162
+ def test_should_compute_total_segment_time
163
+ track = @gpx_file.tracks.first
164
+ puts track.segments.map {|segment| segment.total_time }
165
+ end
166
+
167
+ end
@@ -0,0 +1,3 @@
1
+ require 'minitest/autorun'
2
+ require 'gpx_ruby'
3
+ require 'pp'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gpx_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Rüeedlinger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.6.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.6'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.6.1
33
+ description: A simple gpx file parser for reading gpx files.
34
+ email: a.rueedlinger@gmail.com
35
+ executables: []
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - Rakefile
40
+ - lib/gpx_ruby.rb
41
+ - lib/gpx_ruby/gpx/document.rb
42
+ - lib/gpx_ruby/gpx/parser.rb
43
+ - lib/gpx_ruby/gpx/parsers/track_parser.rb
44
+ - lib/gpx_ruby/gpx/point.rb
45
+ - lib/gpx_ruby/gpx/track.rb
46
+ - lib/version.rb
47
+ - test/test_gpx_parser.rb
48
+ - test/test_helper.rb
49
+ homepage: http://github.com/lexruee/gpx_ruby
50
+ licenses:
51
+ - MIT
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.9.3
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 2.2.2
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: A gpx file parser for reading gpx files.
73
+ test_files: []