From 5aa64f2c3a30bd99616a2a08611b22f7ecb0d851 Mon Sep 17 00:00:00 2001 From: Doug Fales Date: Fri, 30 Nov 2007 06:26:26 +0000 Subject: [PATCH] Updates courtesy of Gaku Ueda: * Adding support for GPX 1.0 as well as 1.1 (since libxml namespace parsing was hard-coded to 1.1. previously). * Adding a GPX 1.0 unit test file. * Miscellaneous updates to make it work with Ruby 1.8.6. --- lib/gpx/gpx.rb | 1 - lib/gpx/gpx_file.rb | 23 +++++++++++++++-------- lib/gpx/point.rb | 5 +++-- lib/gpx/route.rb | 4 ++-- lib/gpx/segment.rb | 5 +++-- lib/gpx/track.rb | 6 +++--- tests/gpx10_test.rb | 12 ++++++++++++ tests/gpx_files/gpx10.gpx | 17 +++++++++++++++++ 8 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 tests/gpx10_test.rb create mode 100644 tests/gpx_files/gpx10.gpx diff --git a/lib/gpx/gpx.rb b/lib/gpx/gpx.rb index b2aa58e..77cd4e9 100644 --- a/lib/gpx/gpx.rb +++ b/lib/gpx/gpx.rb @@ -28,7 +28,6 @@ module GPX class Base include XML -NS = 'gpx:http://www.topografix.com/GPX/1/1' # 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 diff --git a/lib/gpx/gpx_file.rb b/lib/gpx/gpx_file.rb index 8fdf278..3567f2c 100644 --- a/lib/gpx/gpx_file.rb +++ b/lib/gpx/gpx_file.rb @@ -22,7 +22,7 @@ #++ module GPX class GPXFile < Base - attr_reader :tracks, :routes, :waypoints, :bounds, :lowest_point, :highest_point, :distance, :duration, :average_speed + attr_reader :tracks, :routes, :waypoints, :bounds, :lowest_point, :highest_point, :distance, :duration, :average_speed, :ns # This initializer can be used to create a new GPXFile from an existing @@ -50,9 +50,16 @@ module GPX #end gpx_file = gpx_file.name if gpx_file.is_a?(File) reset_meta_data - @xml = Document.file(gpx_file) - - bounds_element = (@xml.find("//gpx:gpx/gpx:metadata/gpx:bounds", NS).to_a.first rescue nil) + @xml = XML::Document.file(gpx_file) + + # set XML namespace for XML find + if @xml.root.namespace_node + @ns = 'gpx:' + @xml.root.namespace_node.href + else + @ns = 'gpx:http://www.topografix.com/GPX/1/1' # default to GPX 1.1 + end + + bounds_element = (@xml.find("//gpx:gpx/gpx:metadata/gpx:bounds", @ns).to_a.first rescue nil) if bounds_element @bounds.min_lat = get_bounds_attr_value(bounds_element, %w{ min_lat minlat minLat }) @bounds.min_lon = get_bounds_attr_value(bounds_element, %w{ min_lon minlon minLon}) @@ -61,17 +68,17 @@ module GPX else get_bounds = true end - + @tracks = [] - @xml.find("//gpx:gpx/gpx:trk", NS).each do |trk| + @xml.find("//gpx:gpx/gpx:trk", @ns).each do |trk| trk = Track.new(:element => trk, :gpx_file => self) update_meta_data(trk, get_bounds) @tracks << trk end @waypoints = [] - @xml.find("//gpx:gpx/gpx:wpt", NS).each { |wpt| @waypoints << Waypoint.new(:element => wpt, :gpx_file => self) } + @xml.find("//gpx:gpx/gpx:wpt", @ns).each { |wpt| @waypoints << Waypoint.new(:element => wpt, :gpx_file => self) } @routes = [] - @xml.find("//gpx:gpx/gpx:rte", NS).each { |rte| @routes << Route.new(:element => rte, :gpx_file => self) } + @xml.find("//gpx:gpx/gpx:rte", @ns).each { |rte| @routes << Route.new(:element => rte, :gpx_file => self) } @tracks.delete_if { |t| t.empty? } diff --git a/lib/gpx/point.rb b/lib/gpx/point.rb index 1343306..9173fea 100644 --- a/lib/gpx/point.rb +++ b/lib/gpx/point.rb @@ -32,13 +32,14 @@ module GPX # addition, you can pass an XML element to this initializer, and the # relevant info will be parsed out. def initialize(opts = {:lat => 0.0, :lon => 0.0, :elevation => 0.0, :time => Time.now } ) + @gpx_file = opts[:gpx_file] if (opts[:element]) elem = opts[:element] @lat, @lon = elem["lat"].to_f, elem["lon"].to_f @latr, @lonr = (D_TO_R * @lat), (D_TO_R * @lon) #'-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)? - @time = (Time.xmlschema(elem.find("gpx:time", NS).first.content) rescue nil) - @elevation = elem.find("gpx:ele", NS).first.content.to_f unless elem.find("gpx:ele", NS).empty? + @time = (Time.xmlschema(elem.find("gpx:time", @gpx_file.ns).first.content) rescue nil) + @elevation = elem.find("gpx:ele", @gpx_file.ns).first.content.to_f unless elem.find("gpx:ele", @gpx_file.ns).empty? else @lat = opts[:lat] @lon = opts[:lon] diff --git a/lib/gpx/route.rb b/lib/gpx/route.rb index 0928ba0..54d0541 100644 --- a/lib/gpx/route.rb +++ b/lib/gpx/route.rb @@ -33,9 +33,9 @@ module GPX def initialize(opts = {}) rte_element = opts[:element] @gpx_file = opts[:gpx_file] - @name = rte_element.find("child::gpx:name", NS).first.content + @name = rte_element.find("child::gpx:name", @ns).first.content @points = [] - rte_element.find("child::gpx:rtept", NS).each do |point| + rte_element.find("child::gpx:rtept", @ns).each do |point| @points << Point.new(:element => point) end diff --git a/lib/gpx/segment.rb b/lib/gpx/segment.rb index 8b4dc9b..b285ddb 100644 --- a/lib/gpx/segment.rb +++ b/lib/gpx/segment.rb @@ -34,6 +34,7 @@ module GPX # If a XML::Node object is passed-in, this will initialize a new # Segment based on its contents. Otherwise, a blank Segment is created. def initialize(opts = {}) + @gpx_file = opts[:gpx_file] @track = opts[:track] @points = [] @earliest_point = nil @@ -46,8 +47,8 @@ module GPX segment_element = opts[:element] last_pt = nil if segment_element.is_a?(XML::Node) - segment_element.find("child::gpx:trkpt", NS).each do |trkpt| - pt = TrackPoint.new(:element => trkpt, :segment => self) + segment_element.find("child::gpx:trkpt", @gpx_file.ns).each do |trkpt| + pt = TrackPoint.new(:element => trkpt, :segment => self, :gpx_file => @gpx_file) unless pt.time.nil? @earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time) @latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time) diff --git a/lib/gpx/track.rb b/lib/gpx/track.rb index 14add8e..daf8585 100644 --- a/lib/gpx/track.rb +++ b/lib/gpx/track.rb @@ -41,9 +41,9 @@ module GPX reset_meta_data if(opts[:element]) trk_element = opts[:element] - @name = (trk_element.find("child::gpx:name", NS).first.content rescue "") - trk_element.find("child::gpx:trkseg", NS).each do |seg_element| - seg = Segment.new(:element => seg_element, :track => self) + @name = (trk_element.find("child::gpx:name", @gpx_file.ns).first.content rescue "") + trk_element.find("child::gpx:trkseg", @gpx_file.ns).each do |seg_element| + 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? diff --git a/tests/gpx10_test.rb b/tests/gpx10_test.rb new file mode 100644 index 0000000..8a98095 --- /dev/null +++ b/tests/gpx10_test.rb @@ -0,0 +1,12 @@ +require 'test/unit' +require File.dirname(__FILE__) + '/../lib/gpx' + +class TestGPX10 < Test::Unit::TestCase + GPX_FILE = File.join(File.dirname(__FILE__), "gpx_files/gpx10.gpx") + + def test_read + # make sure we can read a GPX 1.0 file + @gpx_file = GPX::GPXFile.new(:gpx_file => GPX_FILE) + end + +end diff --git a/tests/gpx_files/gpx10.gpx b/tests/gpx_files/gpx10.gpx new file mode 100644 index 0000000..e9ae08c --- /dev/null +++ b/tests/gpx_files/gpx10.gpx @@ -0,0 +1,17 @@ + + + + ACTIVE LOG #74 + 82 + + + 35.706055 + + + + +