andrewhao-gpx 0.7 → 0.8

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: 5f51764c0a0123d2d6f5a835667a95ec6bee62a6
4
+ data.tar.gz: 18edbd7be186b8c271ab7a75cb3e685688cfd1a6
5
+ SHA512:
6
+ metadata.gz: d163c7cfcc8cbaad5029280365824fd80d89e9fe9100e871b3a595eb2e35108c487ee744990686f6d951e5d8e1bcc3691dd8defbae1826ba49e1df3b3560b355
7
+ data.tar.gz: b46cb4436005b206bb4a833ceecc3acf5d200c0169106da710d00cdc03771e3863e7d73e7a664f930d096252d87bc4643ecccfcdf95f197332e798fb863a37bb
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ - jruby-19mode
8
+ script: "bundle exec rake ci:build"
@@ -0,0 +1,98 @@
1
+ # GPX Gem
2
+
3
+ [<img src="https://travis-ci.org/andrewhao/gpx.svg" alt="Build Status" />](https://travis-ci.org/andrewhao/gpx)
4
+ [![Code Climate](https://codeclimate.com/github/andrewhao/gpx/badges/gpa.svg)](https://codeclimate.com/github/andrewhao/gpx)
5
+
6
+ Copyright (C) 2006 Doug Fales doug@falesafeconsulting.com
7
+
8
+ ## What It Does
9
+
10
+ This library reads GPX files and provides an API for reading and manipulating
11
+ the data as objects. For more info on the GPX format, see
12
+ http://www.topografix.com/gpx.asp.
13
+
14
+ In addition to parsing GPX files, this library is capable of converting
15
+ Magellan NMEA files to GPX, and writing new GPX files. It can crop and delete
16
+ rectangular areas within a file, and it also calculates some meta-data about
17
+ the tracks and points in a file (such as distance, duration, average speed,
18
+ etc).
19
+
20
+ ## Examples
21
+
22
+ Reading a GPX file, and cropping its contents to a given area:
23
+ ```ruby
24
+ gpx = GPX::GPXFile.new(:gpx_file => filename) # Read GPX file
25
+ bounds = GPX::Bounds.new(params) # Create a rectangular area to crop
26
+ gpx.crop(bounds) # Crop it
27
+ gpx.write(filename) # Save it
28
+ ```
29
+
30
+ Converting a Magellan track log to GPX:
31
+ ```ruby
32
+ if GPX::MagellanTrackLog::is_magellan_file?(filename)
33
+ GPX::MagellanTrackLog::convert_to_gpx(filename, "#{filename}.gpx")
34
+ end
35
+ ```
36
+
37
+ Exporting an ActiveRecord to GPXFile (as Waypoints)
38
+ ```ruby
39
+ #
40
+ # Our active record in this example is called stop
41
+ #
42
+
43
+ # models/stop.rb
44
+ class Stop < ActiveRecord::Base
45
+ # This model has the following attributes:
46
+ # name
47
+ # lat
48
+ # lon
49
+ # updated_at
50
+
51
+ def self.to_gpx
52
+ require 'GPX'
53
+ gpx = GPX::GPXFile.new
54
+ all.each do |stop|
55
+ gpx.waypoints << GPX::Waypoint.new({name: stop.name, lat: stop.lat, lon: stop.lon, time: stop.updated_at})
56
+ end
57
+ gpx.to_s
58
+ end
59
+ end # class
60
+
61
+
62
+ # controllers/stops.rb
63
+ def index
64
+ @stops = Stop.all
65
+ respond_to do |format|
66
+ format.html {render :index}
67
+ format.gpx { send_data @stops.to_gpx, filename: controller_name + '.gpx' }
68
+ end
69
+ end
70
+
71
+
72
+ # Add this line to config/initializers/mime_types.rb
73
+ Mime::Type.register "application/gpx+xml", :gpx
74
+
75
+
76
+ # To get the xml file:
77
+ # http://localhost:3000/stops.gpx
78
+ ```
79
+
80
+ You have a complete example on how to create a gpx file from scratch on `tests/output_text.rb`.
81
+
82
+
83
+ ## Notes
84
+
85
+ This library was written to bridge the gap between my Garmin Geko
86
+ and my website, WalkingBoss.org (RIP). For that reason, it has always been more of a
87
+ work-in-progress than an attempt at full GPX compliance. The track side of the
88
+ library has seen much more use than the route/waypoint side, so if you're doing
89
+ something with routes or waypoints, you may need to tweak some things.
90
+
91
+ Since this code uses XML to read an entire GPX file into memory, it is not
92
+ the fastest possible solution for working with GPX data, especially if you are
93
+ working with tracks from several days or weeks.
94
+
95
+ Finally, it should be noted that none of the distance/speed calculation or
96
+ crop/delete code has been tested under International Date Line-crossing
97
+ conditions. That particular part of the code will likely be unreliable if
98
+ you're zig-zagging across 180 degrees longitude routinely.
data/Rakefile CHANGED
@@ -5,6 +5,14 @@ require 'rdoc/task'
5
5
  desc "Default Task"
6
6
  task :default => [ :test ]
7
7
 
8
+ namespace :ci do
9
+ task :build do
10
+ puts "Creating tests/output directory..."
11
+ FileUtils.mkdir_p "tests/output"
12
+ Rake::Task[:test].invoke
13
+ end
14
+ end
15
+
8
16
  # Run the unit tests
9
17
  desc "Run all unit tests"
10
18
  Rake::TestTask.new("test") { |t|
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../../lib/gpx', __FILE__)
4
+
5
+ filename = ARGV[0]
6
+ gpx = GPX::GPXFile.new(:gpx_file => filename)
7
+ puts "read track with distance #{gpx.distance}"
8
+
9
+
10
+
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../../lib/gpx', __FILE__)
4
+ require 'optparse'
5
+
6
+
7
+ def str_to_int_or_time(str)
8
+ if str =~ /\A\d{10}\Z/
9
+ return Time.at(str.to_i)
10
+ elsif str =~ /\A\d+\Z/
11
+ return str.to_i
12
+ else
13
+ return DateTime.strptime(str, '%Y%m%d-%H:%M:%S').to_time
14
+ end
15
+ end
16
+
17
+
18
+ options = {}
19
+ OptionParser.new do |opts|
20
+ opts.banner = "Usage: smooth [options]"
21
+
22
+ opts.on("-i", "--input-file FILE", "Input file to read gpx data from (if omitted, data will be read from stdin)") do |v|
23
+ options[:infile] = v
24
+ end
25
+ opts.on("-o", "--output-file FILE", "Output file to write smoothed gpx data to (if omitted, data will be written to stdout)") do |v|
26
+ options[:outfile] = v
27
+ end
28
+ opts.on("-s", "--start-time [YYYYMMDD-HH:MM:SS|EPOCH|OFFSET]", "Start smoothing from time or offset specified (if omitted start from the start of the file)") do |v|
29
+ options[:start] = v
30
+ end
31
+ opts.on("-e", "--end-time [YYYYMMDD-HH:MM:SS|EPOCH|OFFSET]", "Finish smoothing from time or offset specified (if omitted finish at the end of the file)") do |v|
32
+ options[:end] = v
33
+ end
34
+ end.parse!
35
+
36
+
37
+ if options[:infile]
38
+ input = File.open(options[:infile])
39
+ else
40
+ input = $stdin
41
+ end
42
+
43
+ options[:start] = str_to_int_or_time(options[:start]) if options[:start]
44
+ options[:end] = str_to_int_or_time(options[:end]) if options[:end]
45
+
46
+ gpx = GPX::GPXFile.new(:gpx_data => input)
47
+ $stderr.puts "read track with distance #{gpx.distance}"
48
+
49
+ #1419062980
50
+ gpx.tracks.each do |track|
51
+ track.segments.each do |segment|
52
+ segment.smooth_location_by_average({:end => options[:end], :start => options[:start]})
53
+ end
54
+ end
55
+ gpx.recalculate_distance
56
+ $stderr.puts "smoothed distance #{gpx.distance}"
57
+
58
+ if options[:outfile]
59
+ gpx.write(options[:outfile], false)
60
+ else
61
+ puts gpx.to_s(false)
62
+ end
63
+
@@ -19,4 +19,6 @@ Gem::Specification.new do |s|
19
19
  s.homepage = "http://www.github.com/andrewhao/gpx"
20
20
  s.add_dependency 'rake'
21
21
  s.add_dependency 'nokogiri'
22
+ s.add_development_dependency 'bundler'
23
+ s.add_development_dependency 'minitest'
22
24
  end
data/lib/gpx.rb CHANGED
@@ -24,15 +24,15 @@
24
24
  require 'time'
25
25
  require 'nokogiri'
26
26
 
27
- require 'gpx/version'
27
+ require File.expand_path('../gpx/version', __FILE__)
28
28
 
29
- require 'gpx/gpx'
30
- require 'gpx/gpx_file'
31
- require 'gpx/bounds'
32
- require 'gpx/track'
33
- require 'gpx/route'
34
- require 'gpx/segment'
35
- require 'gpx/point'
36
- require 'gpx/trackpoint'
37
- require 'gpx/waypoint'
38
- require 'gpx/magellan_track_log'
29
+ require File.expand_path('../gpx/gpx', __FILE__)
30
+ require File.expand_path('../gpx/gpx_file', __FILE__)
31
+ require File.expand_path('../gpx/bounds', __FILE__)
32
+ require File.expand_path('../gpx/track', __FILE__)
33
+ require File.expand_path('../gpx/route', __FILE__)
34
+ require File.expand_path('../gpx/segment', __FILE__)
35
+ require File.expand_path('../gpx/point', __FILE__)
36
+ require File.expand_path('../gpx/trackpoint', __FILE__)
37
+ require File.expand_path('../gpx/waypoint', __FILE__)
38
+ require File.expand_path('../gpx/magellan_track_log', __FILE__)
@@ -45,6 +45,8 @@ module GPX
45
45
  #
46
46
  def initialize(opts = {})
47
47
  @duration = 0
48
+ @attributes = {}
49
+ @namespace_defs = []
48
50
  if(opts[:gpx_file] or opts[:gpx_data])
49
51
  if opts[:gpx_file]
50
52
  gpx_file = opts[:gpx_file]
@@ -54,6 +56,13 @@ module GPX
54
56
  @xml = Nokogiri::XML(opts[:gpx_data])
55
57
  end
56
58
 
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']
57
66
  reset_meta_data
58
67
  bounds_element = (@xml.at("metadata/bounds") rescue nil)
59
68
  if bounds_element
@@ -196,7 +205,7 @@ module GPX
196
205
  @time = Time.now if(@time.nil? or update_time)
197
206
  @name ||= File.basename(filename)
198
207
  doc = generate_xml_doc
199
- File.open(filename, 'w') { |f| f.write(doc.to_xml) }
208
+ File.open(filename, 'w+') { |f| f.write(doc.to_xml) }
200
209
  end
201
210
 
202
211
  def to_s(update_time = true)
@@ -209,17 +218,48 @@ module GPX
209
218
  "<#{self.class.name}:...>"
210
219
  end
211
220
 
221
+ def recalculate_distance
222
+ @distance = 0
223
+ @tracks.each do |track|
224
+ track.recalculate_distance
225
+ @distance += track.distance
226
+ end
227
+ end
228
+
212
229
  private
230
+ def attributes_and_nsdefs_as_gpx_attributes
231
+ #$stderr.puts @namespace_defs.inspect
232
+ gpx_header = {}
233
+ @attributes.each do |k,v|
234
+ k = v.namespace.prefix + ':' + k if v.namespace
235
+ gpx_header[k] = v.value
236
+ end
237
+
238
+ @namespace_defs.each do |nsd|
239
+ tag = 'xmlns'
240
+ if nsd.prefix
241
+ tag += ':' + nsd.prefix
242
+ end
243
+ gpx_header[tag] = nsd.href
244
+ end
245
+ return gpx_header
246
+ end
247
+
213
248
  def generate_xml_doc
214
249
  @version ||= '1.1'
215
250
  version_dir = version.gsub('.','/')
216
251
 
252
+ gpx_header = attributes_and_nsdefs_as_gpx_attributes
253
+
254
+ gpx_header['version'] = @version.to_s if !gpx_header['version']
255
+ gpx_header['creator'] = DEFAULT_CREATOR if !gpx_header['creator']
256
+ 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']
257
+ gpx_header['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance" if !gpx_header['xsi'] and !gpx_header['xmlns:xsi']
258
+
259
+ #$stderr.puts gpx_header.keys.inspect
260
+
217
261
  doc = Nokogiri::XML::Builder.new do |xml|
218
- xml.gpx(
219
- 'xsi' => "http://www.w3.org/2001/XMLSchema-instance",
220
- 'version' => @version.to_s,
221
- 'creator' => @creator.nil? ? DEFAULT_CREATOR : @creator.to_s,
222
- 'xsi:schemaLocation' => "http://www.topografix.com/GPX/#{version_dir} http://www.topografix.com/GPX/#{version_dir}/gpx.xsd") \
262
+ xml.gpx(gpx_header) \
223
263
  {
224
264
  # version 1.0 of the schema doesn't support the metadata element, so push them straight to the root 'gpx' element
225
265
  if (@version == '1.0') then
@@ -254,6 +294,7 @@ module GPX
254
294
  xml.trkpt(lat: p.lat, lon: p.lon) {
255
295
  xml.time p.time.xmlschema unless p.time.nil?
256
296
  xml.ele p.elevation unless p.elevation.nil?
297
+ xml << p.extensions.to_xml unless p.extensions.nil?
257
298
  }
258
299
  end
259
300
  }
@@ -263,6 +304,7 @@ module GPX
263
304
 
264
305
  waypoints.each do |w|
265
306
  xml.wpt(lat: w.lat, lon: w.lon) {
307
+ xml.time w.time.xmlschema unless w.time.nil?
266
308
  Waypoint::SUB_ELEMENTS.each do |sub_elem|
267
309
  xml.send(sub_elem, w.send(sub_elem)) if w.respond_to?(sub_elem) && !w.send(sub_elem).nil?
268
310
  end
@@ -24,7 +24,7 @@ module GPX
24
24
  # The base class for all points. Trackpoint and Waypoint both descend from this base class.
25
25
  class Point < Base
26
26
  D_TO_R = Math::PI/180.0;
27
- attr_accessor :lat, :lon, :time, :elevation, :gpx_file, :speed
27
+ attr_accessor :lat, :lon, :time, :elevation, :gpx_file, :speed, :extensions
28
28
 
29
29
  # When you need to manipulate individual points, you can create a Point
30
30
  # object with a latitude, a longitude, an elevation, and a time. In
@@ -40,12 +40,14 @@ module GPX
40
40
  @time = (Time.xmlschema(elem.at("time").inner_text) rescue nil)
41
41
  @elevation = elem.at("ele").inner_text.to_f unless elem.at("ele").nil?
42
42
  @speed = elem.at("speed").inner_text.to_f unless elem.at("speed").nil?
43
+ @extensions = elem.at("extensions") unless elem.at("extensions").nil?
43
44
  else
44
45
  @lat = opts[:lat]
45
46
  @lon = opts[:lon]
46
47
  @elevation = opts[:elevation]
47
48
  @time = opts[:time]
48
49
  @speed = opts[:speed]
50
+ @extensions = opts[:extensions]
49
51
  end
50
52
 
51
53
  end
@@ -48,23 +48,7 @@ module GPX
48
48
  if segment_element.is_a?(Nokogiri::XML::Node)
49
49
  segment_element.search("trkpt").each do |trkpt|
50
50
  pt = TrackPoint.new(:element => trkpt, :segment => self, :gpx_file => @gpx_file)
51
- unless pt.time.nil?
52
- @earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time)
53
- @latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time)
54
- end
55
- unless pt.elevation.nil?
56
- @lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
57
- @highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
58
- end
59
- @bounds.min_lat = pt.lat if pt.lat < @bounds.min_lat
60
- @bounds.min_lon = pt.lon if pt.lon < @bounds.min_lon
61
- @bounds.max_lat = pt.lat if pt.lat > @bounds.max_lat
62
- @bounds.max_lon = pt.lon if pt.lon > @bounds.max_lon
63
-
64
- @distance += haversine_distance(last_pt, pt) unless last_pt.nil?
65
-
66
- @points << pt
67
- last_pt = pt
51
+ append_point(pt)
68
52
  end
69
53
  end
70
54
  end
@@ -73,10 +57,14 @@ module GPX
73
57
  # Tack on a point to this Segment. All meta-data will be updated.
74
58
  def append_point(pt)
75
59
  last_pt = @points[-1]
76
- @earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time)
77
- @latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time)
78
- @lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
79
- @highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
60
+ unless pt.time.nil?
61
+ @earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time)
62
+ @latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time)
63
+ end
64
+ unless pt.elevation.nil?
65
+ @lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
66
+ @highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
67
+ end
80
68
  @bounds.min_lat = pt.lat if pt.lat < @bounds.min_lat
81
69
  @bounds.min_lon = pt.lon if pt.lon < @bounds.min_lon
82
70
  @bounds.max_lat = pt.lat if pt.lat > @bounds.max_lat
@@ -144,6 +132,64 @@ module GPX
144
132
  result
145
133
  end
146
134
 
135
+ def find_point_by_time_or_offset(indicator)
136
+ if indicator.nil?
137
+ return nil
138
+ elsif indicator.is_a?(Integer)
139
+ return closest_point(@earliest_point.time + indicator)
140
+ elsif(indicator.is_a?(Time))
141
+ return closest_point(indicator)
142
+ else
143
+ raise Exception, "find_end_point_by_time_or_offset requires an argument of type Time or Integer"
144
+ end
145
+ end
146
+
147
+ # smooths the location data in the segment (by recalculating the location as an average of 20 neighbouring points. Useful for removing noise from GPS traces.
148
+ def smooth_location_by_average(opts={})
149
+ seconds_either_side = opts[:averaging_window] || 20
150
+
151
+ #calculate the first and last points to which the smoothing should be applied
152
+ earliest = (find_point_by_time_or_offset(opts[:start]) || @earliest_point).time
153
+ latest = (find_point_by_time_or_offset(opts[:end]) || @latest_point).time
154
+
155
+ tmp_points = []
156
+
157
+ @points.each do |point|
158
+ if point.time > latest || point.time < earliest
159
+ tmp_points.push point #add the point unaltered
160
+ next
161
+ end
162
+ lat_av = 0.to_f
163
+ lon_av = 0.to_f
164
+ alt_av = 0.to_f
165
+ n = 0
166
+ # k ranges from the time of the current point +/- 20s
167
+ (-1*seconds_either_side..seconds_either_side).each do |k|
168
+ # find the point nearest to the time offset indicated by k
169
+ contributing_point = closest_point(point.time + k)
170
+ #sum up the contributions to the average
171
+ lat_av += contributing_point.lat
172
+ lon_av += contributing_point.lon
173
+ alt_av += contributing_point.elevation
174
+ n += 1
175
+ end
176
+ # calculate the averages
177
+ tmp_point = point.clone
178
+ tmp_point.lon = ((lon_av) / n).round(7)
179
+ tmp_point.elevation = ((alt_av) / n).round(2)
180
+ tmp_point.lat = ((lat_av) / n).round(7)
181
+ tmp_points.push tmp_point
182
+ end
183
+ last_pt = nil
184
+ @distance = 0
185
+ @points.clear
186
+ reset_meta_data
187
+ #now commit the averages back and recalculate the distances
188
+ tmp_points.each do |point|
189
+ append_point(point)
190
+ end
191
+ end
192
+
147
193
  protected
148
194
  def find_closest(pts, time)
149
195
  return pts.first if pts.size == 1
@@ -114,6 +114,13 @@ module GPX
114
114
  result
115
115
  end
116
116
 
117
+ def recalculate_distance
118
+ @distance = 0
119
+ @segments.each do |seg|
120
+ @distance += seg.distance
121
+ end
122
+ end
123
+
117
124
  protected
118
125
 
119
126
  def update_meta_data(seg)
@@ -30,6 +30,7 @@ module GPX
30
30
 
31
31
  attr_accessor :segment
32
32
 
33
+
33
34
  def initialize(opts = {})
34
35
  super(opts)
35
36
  @segment = opts[:segment]
@@ -54,5 +55,6 @@ module GPX
54
55
  def law_of_cosines_distance_from(p2)
55
56
  (Math.acos(Math.sin(latr)*Math.sin(p2.latr) + Math.cos(latr)*Math.cos(p2.latr)*Math.cos(p2.lonr-lonr)) * RADIUS)
56
57
  end
58
+
57
59
  end
58
60
  end
@@ -1,3 +1,3 @@
1
1
  module GPX
2
- VERSION = "0.7"
2
+ VERSION = "0.8"
3
3
  end
@@ -25,7 +25,7 @@ module GPX
25
25
  # not seen much use yet, since WalkingBoss does not use waypoints right now.
26
26
  class Waypoint < Point
27
27
 
28
- SUB_ELEMENTS = %w{ magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid extensions ele}
28
+ SUB_ELEMENTS = %w{ele magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid extensions}
29
29
 
30
30
  attr_reader :gpx_file
31
31
  SUB_ELEMENTS.each { |sub_el| attr_accessor sub_el.to_sym }
@@ -1,7 +1,7 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
- class GPX10Test < Test::Unit::TestCase
4
+ class GPX10Test < Minitest::Test
5
5
  GPX_FILE = File.join(File.dirname(__FILE__), "gpx_files/gpx10.gpx")
6
6
 
7
7
  def test_read
@@ -1,7 +1,7 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
- class GPXFileTest < Test::Unit::TestCase
4
+ class GPXFileTest < Minitest::Test
5
5
 
6
6
  ONE_TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/one_track.gpx")
7
7
  WITH_OR_WITHOUT_ELEV_FILE = File.join(File.dirname(__FILE__), "gpx_files/with_or_without_elev.gpx")
@@ -17,4 +17,6 @@
17
17
  <wpt lat="39.999840" lon="-105.214696"><name><![CDATA[SBDR]]></name><sym>Waypoint</sym><ele>1612.965</ele></wpt>
18
18
  <wpt lat="39.989739" lon="-105.295285"><name><![CDATA[TO]]></name><sym>Waypoint</sym><ele>2163.556</ele></wpt>
19
19
  <wpt lat="40.035301" lon="-105.254443"><name><![CDATA[VICS]]></name><sym>Waypoint</sym><ele>1535.34</ele></wpt>
20
+ <wpt lat="8.992441" lon="-79.530365"><time>2015-01-01T12:12:12Z</time><name>loma la pava ???</name><desc>Mar 22, 2015, 10:46:17</desc>
21
+ </wpt>
20
22
  </gpx>
@@ -1,7 +1,7 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
- class MagellanTest < Test::Unit::TestCase
4
+ class MagellanTest < Minitest::Test
5
5
  MAGELLAN_TRACK_LOG = File.join(File.dirname(__FILE__), "gpx_files/magellan_track.log")
6
6
  GPX_FILE = File.join(File.dirname(__FILE__), "gpx_files/one_segment.gpx")
7
7
 
@@ -1,8 +1,8 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'fileutils'
3
3
  require 'gpx'
4
4
 
5
- class OutputTest < Test::Unit::TestCase
5
+ class OutputTest < Minitest::Test
6
6
 
7
7
  include GPX
8
8
 
@@ -42,7 +42,8 @@ class OutputTest < Test::Unit::TestCase
42
42
  {:lat => 25.061783, :lon => 121.640267, :name => 'GRMTWN', :sym => 'Waypoint', :ele => '38.09766'},
43
43
  {:lat => 39.999840, :lon => -105.214696, :name => 'SBDR', :sym => 'Waypoint', :ele => '1612.965'},
44
44
  {:lat => 39.989739, :lon => -105.295285, :name => 'TO', :sym => 'Waypoint', :ele => '2163.556'},
45
- {:lat => 40.035301, :lon => -105.254443, :name => 'VICS', :sym => 'Waypoint', :ele => '1535.34'}
45
+ {:lat => 40.035301, :lon => -105.254443, :name => 'VICS', :sym => 'Waypoint', :ele => '1535.34'},
46
+ {:lat => 40.035301, :lon => -105.254443, :name => 'TIMEDWPT', :sym => 'Waypoint', :ele => '1535.34', :time => Time.parse("2005-12-31T22:05:09Z")}
46
47
  ]
47
48
 
48
49
  waypoint_data.each do |wpt_hash|
@@ -1,7 +1,7 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
- class RouteTest < Test::Unit::TestCase
4
+ class RouteTest < Minitest::Test
5
5
 
6
6
  def test_read_routes
7
7
  gpx = GPX::GPXFile.new(:gpx_file => File.join(File.dirname(__FILE__), "gpx_files/routes.gpx"))
@@ -1,8 +1,9 @@
1
- require 'test/unit'
1
+ #require 'minitest/autorun'
2
+ require 'minitest/autorun'
2
3
  require 'yaml'
3
4
  require 'gpx'
4
5
 
5
- class SegmentTest < Test::Unit::TestCase
6
+ class SegmentTest < Minitest::Test
6
7
  ONE_SEGMENT = File.join(File.dirname(__FILE__), "gpx_files/one_segment.gpx")
7
8
 
8
9
  def setup
@@ -55,4 +56,35 @@ class SegmentTest < Test::Unit::TestCase
55
56
  assert_equal(39.188747, @segment.bounds.max_lat)
56
57
  assert_equal(-109.007978, @segment.bounds.max_lon)
57
58
  end
59
+
60
+ def test_segment_smooth
61
+ @segment.smooth_location_by_average
62
+ assert_equal(189, @segment.points.size)
63
+ assert_equal(1144433525, @segment.earliest_point.time.to_i)
64
+ assert_equal(1144437991, @segment.latest_point.time.to_i)
65
+ assert_equal(1342.58, @segment.lowest_point.elevation)
66
+ assert_equal(1479.09, @segment.highest_point.elevation)
67
+ assert_in_delta(6.458085658, @segment.distance, 0.001)
68
+ end
69
+
70
+ def test_segment_smooth_offset
71
+ @segment.smooth_location_by_average({:start => 1000, :end => 2000})
72
+ assert_equal(189, @segment.points.size)
73
+ assert_equal(1144433525, @segment.earliest_point.time.to_i)
74
+ assert_equal(1144437991, @segment.latest_point.time.to_i)
75
+ assert_equal(1334.447, @segment.lowest_point.elevation)
76
+ assert_equal(1480.087, @segment.highest_point.elevation)
77
+ assert_in_delta(6.900813095, @segment.distance, 0.001)
78
+ end
79
+
80
+ def test_segment_smooth_absolute
81
+ @segment.smooth_location_by_average({:start => Time.at(1144434520), :end => Time.at(1144435520)})
82
+ assert_equal(189, @segment.points.size)
83
+ assert_equal(1144433525, @segment.earliest_point.time.to_i)
84
+ assert_equal(1144437991, @segment.latest_point.time.to_i)
85
+ assert_equal(1334.447, @segment.lowest_point.elevation)
86
+ assert_equal(1480.087, @segment.highest_point.elevation)
87
+ assert_in_delta(6.900813095, @segment.distance, 0.001)
88
+ end
89
+
58
90
  end
@@ -1,7 +1,7 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
- class TrackFileTest < Test::Unit::TestCase
4
+ class TrackFileTest < Minitest::Test
5
5
  TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/tracks.gpx")
6
6
  OTHER_TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/arches.gpx")
7
7
 
@@ -1,7 +1,7 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
- class TrackPointTest < Test::Unit::TestCase
4
+ class TrackPointTest < Minitest::Test
5
5
  def setup
6
6
  @point1 = GPX::TrackPoint.new({
7
7
  :lat => 37.7985474,
@@ -1,7 +1,7 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
- class TrackTest < Test::Unit::TestCase
4
+ class TrackTest < Minitest::Test
5
5
  ONE_TRACK = File.join(File.dirname(__FILE__), "gpx_files/one_track.gpx")
6
6
 
7
7
  def setup
@@ -1,12 +1,12 @@
1
- require 'test/unit'
1
+ require 'minitest/autorun'
2
2
  require 'gpx'
3
3
 
4
- class WaypointTest < Test::Unit::TestCase
4
+ class WaypointTest < Minitest::Test
5
5
 
6
6
  def test_read_waypoints
7
7
 
8
8
  gpx = GPX::GPXFile.new(:gpx_file => File.join(File.dirname(__FILE__), "gpx_files/waypoints.gpx"))
9
- assert_equal(17, gpx.waypoints.size)
9
+ assert_equal(18, gpx.waypoints.size)
10
10
 
11
11
  # First Waypoint
12
12
  # <wpt lat="40.035557" lon="-105.248268">
@@ -38,7 +38,11 @@ class WaypointTest < Test::Unit::TestCase
38
38
  assert_equal('002', second_wpt.name)
39
39
  assert_equal('Waypoint', second_wpt.sym)
40
40
  assert_equal(1955.192, second_wpt.elevation)
41
-
41
+
42
+ # test loads time properly in waypoint
43
+ time_wpt = gpx.waypoints[17]
44
+ assert_equal(Time.xmlschema("2015-01-01T12:12:12Z"), time_wpt.time)
45
+
42
46
  end
43
47
  end
44
48
 
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: andrewhao-gpx
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.7'
5
- prerelease:
4
+ version: '0.8'
6
5
  platform: ruby
7
6
  authors:
8
7
  - Guillaume Dott
@@ -11,38 +10,62 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2014-11-08 00:00:00.000000000 Z
13
+ date: 2015-04-20 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: rake
18
17
  requirement: !ruby/object:Gem::Requirement
19
- none: false
20
18
  requirements:
21
- - - ! '>='
19
+ - - ">="
22
20
  - !ruby/object:Gem::Version
23
21
  version: '0'
24
22
  type: :runtime
25
23
  prerelease: false
26
24
  version_requirements: !ruby/object:Gem::Requirement
27
- none: false
28
25
  requirements:
29
- - - ! '>='
26
+ - - ">="
30
27
  - !ruby/object:Gem::Version
31
28
  version: '0'
32
29
  - !ruby/object:Gem::Dependency
33
30
  name: nokogiri
34
31
  requirement: !ruby/object:Gem::Requirement
35
- none: false
36
32
  requirements:
37
- - - ! '>='
33
+ - - ">="
38
34
  - !ruby/object:Gem::Version
39
35
  version: '0'
40
36
  type: :runtime
41
37
  prerelease: false
42
38
  version_requirements: !ruby/object:Gem::Requirement
43
- none: false
44
39
  requirements:
45
- - - ! '>='
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: bundler
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: minitest
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
46
69
  - !ruby/object:Gem::Version
47
70
  version: '0'
48
71
  description: A basic API for reading and writing GPX files.
@@ -54,11 +77,14 @@ executables: []
54
77
  extensions: []
55
78
  extra_rdoc_files: []
56
79
  files:
57
- - .gitignore
80
+ - ".gitignore"
81
+ - ".travis.yml"
58
82
  - ChangeLog
59
83
  - Gemfile
60
- - README.rdoc
84
+ - README.md
61
85
  - Rakefile
86
+ - bin/gpx_distance
87
+ - bin/gpx_smooth
62
88
  - gpx.gemspec
63
89
  - lib/gpx.rb
64
90
  - lib/gpx/bounds.rb
@@ -95,32 +121,26 @@ files:
95
121
  - tests/waypoint_test.rb
96
122
  homepage: http://www.github.com/andrewhao/gpx
97
123
  licenses: []
124
+ metadata: {}
98
125
  post_install_message:
99
126
  rdoc_options: []
100
127
  require_paths:
101
128
  - lib
102
129
  required_ruby_version: !ruby/object:Gem::Requirement
103
- none: false
104
130
  requirements:
105
- - - ! '>='
131
+ - - ">="
106
132
  - !ruby/object:Gem::Version
107
133
  version: '0'
108
- segments:
109
- - 0
110
- hash: -3842906676439924695
111
134
  required_rubygems_version: !ruby/object:Gem::Requirement
112
- none: false
113
135
  requirements:
114
- - - ! '>='
136
+ - - ">="
115
137
  - !ruby/object:Gem::Version
116
138
  version: '0'
117
- segments:
118
- - 0
119
- hash: -3842906676439924695
120
139
  requirements: []
121
140
  rubyforge_project:
122
- rubygems_version: 1.8.23
141
+ rubygems_version: 2.4.5
123
142
  signing_key:
124
- specification_version: 3
143
+ specification_version: 4
125
144
  summary: A basic API for reading and writing GPX files.
126
145
  test_files: []
146
+ has_rdoc: true
@@ -1,46 +0,0 @@
1
- = GPX Gem
2
-
3
- Copyright (C) 2006 Doug Fales mailto:doug@falesafeconsulting.com
4
-
5
- == What It Does
6
-
7
- This library reads GPX files and provides an API for reading and manipulating
8
- the data as objects. For more info on the GPX format, see
9
- http://www.topografix.com/gpx.asp.
10
-
11
- In addition to parsing GPX files, this library is capable of converting
12
- Magellan NMEA files to GPX, and writing new GPX files. It can crop and delete
13
- rectangular areas within a file, and it also calculates some meta-data about
14
- the tracks and points in a file (such as distance, duration, average speed,
15
- etc).
16
-
17
- == Examples
18
-
19
- Reading a GPX file, and cropping its contents to a given area:
20
- gpx = GPX::GPXFile.new(:gpx_file => filename) # Read GPX file
21
- bounds = GPX::Bounds.new(params) # Create a rectangular area to crop
22
- gpx.crop(bounds) # Crop it
23
- gpx.write(filename) # Save it
24
-
25
- Converting a Magellan track log to GPX:
26
- if GPX::MagellanTrackLog::is_magellan_file?(filename)
27
- GPX::MagellanTrackLog::convert_to_gpx(filename, "#{filename}.gpx")
28
- end
29
-
30
-
31
- == Notes
32
-
33
- This library was written to bridge the gap between my Garmin Geko
34
- and my website, WalkingBoss.org (RIP). For that reason, it has always been more of a
35
- work-in-progress than an attempt at full GPX compliance. The track side of the
36
- library has seen much more use than the route/waypoint side, so if you're doing
37
- something with routes or waypoints, you may need to tweak some things.
38
-
39
- Since this code uses XML to read an entire GPX file into memory, it is not
40
- the fastest possible solution for working with GPX data, especially if you are
41
- working with tracks from several days or weeks.
42
-
43
- Finally, it should be noted that none of the distance/speed calculation or
44
- crop/delete code has been tested under International Date Line-crossing
45
- conditions. That particular part of the code will likely be unreliable if
46
- you're zig-zagging across 180 degrees longitude routinely.