Adding some new unit tests and fixing several file export bugs reported by Jochen Topf.

New unit tests also uncovered a bug where the number of trackpoints reported in a file was twice the actual number.
master
Doug Fales 2008-02-19 04:06:29 +00:00
parent ef1eac8cbb
commit 3abba74eef
10 changed files with 217 additions and 51 deletions

View File

@ -21,30 +21,30 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
module GPX
VERSION = "0.3"
VERSION = "0.3"
# A common base class which provides a useful initializer method to many
# class in the GPX library.
class Base
include XML
# A common base class which provides a useful initializer method to many
# class in the GPX library.
class Base
include XML
# This initializer can take an XML::Node and scrape out any text
# elements with the names given in the "text_elements" array. Each
# element found underneath "parent" with a name in "text_elements" causes
# an attribute to be initialized on the instance. This means you don't
# have to pick out individual text elements in each initializer of each
# class (Route, TrackPoint, Track, etc). Just pass an array of possible
# attributes to this method.
def instantiate_with_text_elements(parent, text_elements, ns)
text_elements.each do |el|
child_xpath = "gpx:#{el}"
unless parent.find(child_xpath, ns).empty?
val = parent.find(child_xpath, ns).first.content
self.send("#{el}=", val)
end
end
# This initializer can take an XML::Node and scrape out any text
# elements with the names given in the "text_elements" array. Each
# element found underneath "parent" with a name in "text_elements" causes
# an attribute to be initialized on the instance. This means you don't
# have to pick out individual text elements in each initializer of each
# class (Route, TrackPoint, Track, etc). Just pass an array of possible
# attributes to this method.
def instantiate_with_text_elements(parent, text_elements, ns)
text_elements.each do |el|
child_xpath = "gpx:#{el}"
unless parent.find(child_xpath, ns).empty?
val = parent.find(child_xpath, ns).first.content
self.send("#{el}=", val)
end
end
end
end
end
end
end

View File

@ -22,7 +22,7 @@
#++
module GPX
class GPXFile < Base
attr_reader :tracks, :routes, :waypoints, :bounds, :lowest_point, :highest_point, :distance, :duration, :average_speed, :ns
attr_accessor :tracks, :routes, :waypoints, :bounds, :lowest_point, :highest_point, :duration, :ns, :time, :name
# This initializer can be used to create a new GPXFile from an existing
@ -75,6 +75,9 @@ module GPX
else
get_bounds = true
end
@time = Time.parse(@xml.find("//gpx:gpx/gpx:metadata/gpx:time", @ns).first.content) rescue nil
@name = @xml.find("//gpx:gpx/gpx:metadata/gpx:name", @ns).first.content rescue nil
@tracks = []
@xml.find("//gpx:gpx/gpx:trk", @ns).each do |trk|
@ -98,6 +101,9 @@ module GPX
calculate_duration
end
end
@tracks ||= []
@routes ||= []
@waypoints ||= []
end
def get_bounds_attr_value(el, possible_names)
@ -199,7 +205,7 @@ module GPX
# Serialize the current GPXFile to a gpx file named <filename>.
# If the file does not exist, it is created. If it does exist, it is overwritten.
def write(filename)
def write(filename, update_time = true)
doc = Document.new
doc.root = Node.new('gpx')
@ -216,7 +222,10 @@ module GPX
meta_data_elem << name_elem
time_elem = Node.new('time')
time_elem << Time.now.xmlschema
@time = Time.now if(@time.nil? or update_time)
time_elem << @time.xmlschema
meta_data_elem << time_elem
meta_data_elem << bounds.to_xml

View File

@ -88,7 +88,7 @@ module GPX
# Convert this point to a XML::Node.
def to_xml(elem_name = 'trkpt')
pt = Node.new('trkpt')
pt = Node.new(elem_name)
pt['lat'] = lat.to_s
pt['lon'] = lon.to_s
unless time.nil?

View File

@ -27,17 +27,22 @@ module GPX
# automatically logging your progress at regular intervals.
class Route < Base
attr_reader :points, :name, :gpx_file
attr_accessor :points, :name, :gpx_file
# Initialize a Route from a XML::Node.
def initialize(opts = {})
if(opts[:gpx_file] and opts[:element])
rte_element = opts[:element]
@gpx_file = opts[:gpx_file]
@name = rte_element.find("child::gpx:name", @gpx_file.ns).first.content
@points = []
rte_element.find("child::gpx:rtept", @gpx_file.ns).each do |point|
@points << Point.new(:element => point, :gpx_file => @gpx_file)
@points << Point.new(:element => point, :gpx_file => @gpx_file)
end
else
@points = (opts[:points] or [])
@name = (opts[:name])
end
end

View File

@ -46,7 +46,6 @@ module GPX
seg = Segment.new(:element => seg_element, :track => self, :gpx_file => @gpx_file)
update_meta_data(seg)
@segments << seg
@points.concat(seg.points) unless seg.nil?
end
end
end

View File

@ -42,10 +42,19 @@ module GPX
# Initializes a waypoint from a XML::Node.
def initialize(opts = {})
wpt_elem = opts[:element]
@gpx_file = opts[:gpx_file]
super(:element => wpt_elem, :gpx_file => @gpx_file)
instantiate_with_text_elements(wpt_elem, SUB_ELEMENTS, @gpx_file.ns)
if(opts[:element] and opts[:gpx_file])
wpt_elem = opts[:element]
@gpx_file = opts[:gpx_file]
super(:element => wpt_elem, :gpx_file => @gpx_file)
instantiate_with_text_elements(wpt_elem, SUB_ELEMENTS, @gpx_file.ns)
else
opts.each do |key, value|
assignment_method = "#{key}="
if self.respond_to?(assignment_method)
self.send(assignment_method, value)
end
end
end
end
# Prints out a friendly summary of this track (sans points). Useful for
@ -67,22 +76,19 @@ module GPX
# Converts a waypoint to a XML::Node.
def to_xml
wpt = Node.new('wpt')
wpt.attributes['lat'] = lat
wpt.attributes['lon'] = lon
if self.respond_to? :name
name_elem = Node.new('name')
name_elem << self.name
wpt << name_elem
wpt['lat'] = lat.to_s
wpt['lon'] = lon.to_s
SUB_ELEMENTS.each do |sub_element_name|
if(self.respond_to?(sub_element_name) and (!self.send(sub_element_name).nil?))
sub_elem_node = Node.new(sub_element_name)
sub_elem_node << self.send(sub_element_name)
wpt << sub_elem_node
end
end
if self.respond_to? :sym
sym_elem = Node.new('sym')
sym_elem << self.sym
wpt << sym_elem
end
if self.respond_to? :ele
elev_elem = Node.new('ele')
elev_elem << self.ele
wpt << elev_elem
unless(self.elevation.nil?)
ele_node = Node.new('ele')
ele_node << self.elevation
wpt << ele_node
end
wpt
end

View File

@ -2,8 +2,40 @@ require 'test/unit'
require File.dirname(__FILE__) + '/../lib/gpx'
class GPXFileTest < Test::Unit::TestCase
ONE_TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/one_track.gpx")
def test_load_data
GPX::GPXFile.new(:gpx_data => open(ONE_TRACK_FILE).read)
BIG_FILE = File.join(File.dirname(__FILE__), "gpx_files/big.gpx")
def test_load_data_from_string
gpx_file = GPX::GPXFile.new(:gpx_data => open(ONE_TRACK_FILE).read)
assert_equal(1, gpx_file.tracks.size)
assert_equal(8, gpx_file.tracks.first.segments.size)
assert_equal("ACTIVE LOG", gpx_file.tracks.first.name)
assert_equal("active_log.gpx", gpx_file.name)
assert_equal("2006-04-08T16:44:28Z", gpx_file.time.xmlschema)
assert_equal(38.681488, gpx_file.bounds.min_lat)
assert_equal(-109.606948, gpx_file.bounds.min_lon)
assert_equal(38.791759, gpx_file.bounds.max_lat)
assert_equal(-109.447045, gpx_file.bounds.max_lon)
end
def test_load_data_from_file
gpx_file = GPX::GPXFile.new(:gpx_file => ONE_TRACK_FILE)
assert_equal(1, gpx_file.tracks.size)
assert_equal(8, gpx_file.tracks.first.segments.size)
assert_equal("ACTIVE LOG", gpx_file.tracks.first.name)
assert_equal("active_log.gpx", gpx_file.name)
assert_equal("2006-04-08T16:44:28Z", gpx_file.time.xmlschema)
assert_equal(38.681488, gpx_file.bounds.min_lat)
assert_equal(-109.606948, gpx_file.bounds.min_lon)
assert_equal(38.791759, gpx_file.bounds.max_lat)
assert_equal(-109.447045, gpx_file.bounds.max_lon)
end
def test_big_file
gpx_file = GPX::GPXFile.new(:gpx_file => BIG_FILE)
assert_equal(1, gpx_file.tracks.size)
assert_equal(7968, gpx_file.tracks.first.points.size)
end
end

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,114 @@
require 'test/unit'
require File.dirname(__FILE__) + '/../lib/gpx'
class OutputTest < Test::Unit::TestCase
include GPX
def test_new_gpx_file_from_scratch
gpx_file = GPXFile.new
track = Track.new(:name => "My First Track")
segment = Segment.new
track_point_data = [
{:lat => 40.036926, :lon =>-105.253487, :time => Time.parse('2005-12-31T22:01:24Z'), :elevation => 1737.24},
{:lat => 40.036604, :lon =>-105.253487, :time => Time.parse("2005-12-31T22:02:01Z"), :elevation => 1738.682},
{:lat => 40.036347, :lon =>-105.253830, :time => Time.parse("2005-12-31T22:02:08Z"), :elevation => 1738.682},
{:lat => 40.035574, :lon =>-105.254045, :time => Time.parse("2005-12-31T22:02:20Z"), :elevation => 1737.24},
{:lat => 40.035467, :lon =>-105.254366, :time => Time.parse("2005-12-31T22:02:29Z"), :elevation => 1735.798},
{:lat => 40.035317, :lon =>-105.254388, :time => Time.parse("2005-12-31T22:02:33Z"), :elevation => 1735.798},
{:lat => 40.035274, :lon =>-105.254431, :time => Time.parse("2005-12-31T22:02:49Z"), :elevation => 1736.278},
{:lat => 40.035274, :lon =>-105.254431, :time => Time.parse("2005-12-31T22:02:54Z"), :elevation => 1739.643},
{:lat => 40.035317, :lon =>-105.254431, :time => Time.parse("2005-12-31T22:05:08Z"), :elevation => 1732.433},
{:lat => 40.035317, :lon =>-105.254431, :time => Time.parse("2005-12-31T22:05:09Z"), :elevation => 1726.665}]
track_point_data.each do |trk_pt_hash|
segment.points << TrackPoint.new(trk_pt_hash)
end
track.segments << segment
gpx_file.tracks << track
waypoint_data = [
{:lat => 39.997298, :lon => -105.292674, :name => 'GRG-CA', :sym => 'Waypoint', :elevation => 1766.535},
{:lat => 33.330190, :lon => -111.946110, :name => 'GRMPHX', :sym => 'Waypoint', :elevation => 361.0981,
:cmt => "Hey here's a comment.", :desc => "Somewhere in my backyard.", :fix => '3d', :sat => "8", :hdop => "50.5", :vdop => "6.8", :pdop => "7.6"},
{:lat => 25.061783, :lon => 121.640267, :name => 'GRMTWN', :sym => 'Waypoint', :elevation => 38.09766},
{:lat => 39.999840, :lon => -105.214696, :name => 'SBDR', :sym => 'Waypoint', :elevation => 1612.965},
{:lat => 39.989739, :lon => -105.295285, :name => 'TO', :sym => 'Waypoint', :elevation => 2163.556},
{:lat => 40.035301, :lon => -105.254443, :name => 'VICS', :sym => 'Waypoint', :elevation => 1535.34}
]
waypoint_data.each do |wpt_hash|
gpx_file.waypoints << Waypoint.new(wpt_hash)
end
route_point_data = [
{:lat => 40.035467, :lon =>-105.254366, :time => Time.parse("2005-12-31T22:02:29Z"), :elevation => 1735.798},
{:lat => 40.035317, :lon =>-105.254388, :time => Time.parse("2005-12-31T22:02:33Z"), :elevation => 1735.798},
{:lat => 40.035274, :lon =>-105.254431, :time => Time.parse("2005-12-31T22:02:49Z"), :elevation => 1736.278} ]
route = Route.new()
route_point_data.each do |rte_pt_hash|
route.points << Point.new(rte_pt_hash)
end
gpx_file.routes << route
gpx_file.write(output_file(name_of_test))
written_gpx_file = GPXFile.new(:gpx_file => output_file(name_of_test))
assert_equal(File.basename(output_file(name_of_test)), written_gpx_file.name)
assert_equal(1, written_gpx_file.tracks.size)
assert_equal(1, written_gpx_file.tracks[0].segments.size)
assert_equal(track_point_data.size, written_gpx_file.tracks[0].segments[0].points.size)
assert_equal(track_point_data.size, written_gpx_file.tracks[0].points.size)
# Make sure each point in the segment has the attributes it was initialized with
written_segment = written_gpx_file.tracks[0].segments[0]
track_point_data.each_with_index do |trk_pt_hash, index|
trk_pt_hash.each do |key, value|
assert_equal(value, written_segment.points[index].send(key))
end
end
# Make sure the one route has the attributes we initialized it with
assert_equal(1, written_gpx_file.routes.size)
written_route = written_gpx_file.routes[0]
assert_equal(route_point_data.size, written_route.points.size)
route_point_data.each_with_index do |rte_pt_hash, index|
rte_pt_hash.each do |key, value|
assert_equal(value, written_route.points[index].send(key))
end
end
# Make sure the waypoints have all of the attributes we initialized them with
written_waypoints = written_gpx_file.waypoints
assert_equal(waypoint_data.size, written_waypoints.size)
waypoint_data.each_with_index do |wpt_hash, index|
wpt_hash.each do |key, value|
assert_equal(value, written_waypoints[index].send(key.to_s), key)
end
end
expected_value = sprintf(THE_WORKS, gpx_file.time.xmlschema)
assert_equal(expected_value, IO.read(output_file(name_of_test)))
end
def name_of_test
caller[0] =~ /`test_([^']*)'/ and $1
end
def output_file(test_name)
File.join(File.dirname(__FILE__), "output/#{test_name}.gpx")
end
THE_WORKS = "<?xml version=\"1.0\"?>\n<gpx xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/1\" version=\"1.1\" creator=\"GPX RubyGem 0.1 Copyright 2006 Doug Fales -- http://walkingboss.com\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n <metadata>\n <name>new_gpx_file_from_scratch.gpx</name>\n <time>%s</time>\n <bounds minlat=\"90.0\" minlon=\"180.0\" maxlat=\"-90.0\" maxlon=\"-180.0\"/>\n </metadata>\n <trk>\n <name/>\n <trkseg>\n <trkpt lat=\"40.036926\" lon=\"-105.253487\">\n <time>2005-12-31T22:01:24Z</time>\n <ele>1737.24</ele>\n </trkpt>\n <trkpt lat=\"40.036604\" lon=\"-105.253487\">\n <time>2005-12-31T22:02:01Z</time>\n <ele>1738.682</ele>\n </trkpt>\n <trkpt lat=\"40.036347\" lon=\"-105.25383\">\n <time>2005-12-31T22:02:08Z</time>\n <ele>1738.682</ele>\n </trkpt>\n <trkpt lat=\"40.035574\" lon=\"-105.254045\">\n <time>2005-12-31T22:02:20Z</time>\n <ele>1737.24</ele>\n </trkpt>\n <trkpt lat=\"40.035467\" lon=\"-105.254366\">\n <time>2005-12-31T22:02:29Z</time>\n <ele>1735.798</ele>\n </trkpt>\n <trkpt lat=\"40.035317\" lon=\"-105.254388\">\n <time>2005-12-31T22:02:33Z</time>\n <ele>1735.798</ele>\n </trkpt>\n <trkpt lat=\"40.035274\" lon=\"-105.254431\">\n <time>2005-12-31T22:02:49Z</time>\n <ele>1736.278</ele>\n </trkpt>\n <trkpt lat=\"40.035274\" lon=\"-105.254431\">\n <time>2005-12-31T22:02:54Z</time>\n <ele>1739.643</ele>\n </trkpt>\n <trkpt lat=\"40.035317\" lon=\"-105.254431\">\n <time>2005-12-31T22:05:08Z</time>\n <ele>1732.433</ele>\n </trkpt>\n <trkpt lat=\"40.035317\" lon=\"-105.254431\">\n <time>2005-12-31T22:05:09Z</time>\n <ele>1726.665</ele>\n </trkpt>\n </trkseg>\n </trk>\n <wpt lat=\"39.997298\" lon=\"-105.292674\">\n <name>GRG-CA</name>\n <sym>Waypoint</sym>\n <ele>1766.535</ele>\n </wpt>\n <wpt lat=\"33.33019\" lon=\"-111.94611\">\n <name>GRMPHX</name>\n <cmt>Hey here's a comment.</cmt>\n <desc>Somewhere in my backyard.</desc>\n <sym>Waypoint</sym>\n <fix>3d</fix>\n <sat>8</sat>\n <hdop>50.5</hdop>\n <vdop>6.8</vdop>\n <pdop>7.6</pdop>\n <ele>361.0981</ele>\n </wpt>\n <wpt lat=\"25.061783\" lon=\"121.640267\">\n <name>GRMTWN</name>\n <sym>Waypoint</sym>\n <ele>38.09766</ele>\n </wpt>\n <wpt lat=\"39.99984\" lon=\"-105.214696\">\n <name>SBDR</name>\n <sym>Waypoint</sym>\n <ele>1612.965</ele>\n </wpt>\n <wpt lat=\"39.989739\" lon=\"-105.295285\">\n <name>TO</name>\n <sym>Waypoint</sym>\n <ele>2163.556</ele>\n </wpt>\n <wpt lat=\"40.035301\" lon=\"-105.254443\">\n <name>VICS</name>\n <sym>Waypoint</sym>\n <ele>1535.34</ele>\n </wpt>\n <rte>\n <name/>\n <rtept lat=\"40.035467\" lon=\"-105.254366\">\n <time>2005-12-31T22:02:29Z</time>\n <ele>1735.798</ele>\n </rtept>\n <rtept lat=\"40.035317\" lon=\"-105.254388\">\n <time>2005-12-31T22:02:33Z</time>\n <ele>1735.798</ele>\n </rtept>\n <rtept lat=\"40.035274\" lon=\"-105.254431\">\n <time>2005-12-31T22:02:49Z</time>\n <ele>1736.278</ele>\n </rtept>\n </rte>\n</gpx>\n"
end

View File

@ -11,7 +11,7 @@ class TrackTest < Test::Unit::TestCase
def test_track_read
assert_equal("ACTIVE LOG", @track.name)
assert_equal( 364, @track.points.size)
assert_equal( 182, @track.points.size)
assert_equal(8, @track.segments.size)
assert_equal("3.07249668492626", @track.distance.to_s)
assert_equal(1267.155, @track.lowest_point.elevation)