Compare commits

...

No commits in common. "master" and "release-0.1" have entirely different histories.

33 changed files with 930 additions and 1328 deletions

3
.gitignore vendored
View File

@ -1,3 +0,0 @@
tests/output/*
.*.swp
Gemfile.lock

View File

@ -1,60 +0,0 @@
2010-02-27 Doug Fales <doug@falesafeconsulting.com>
* Putting the gem building stuff into a gemspec.
* Fixing some tests since git does not believe in empty directories.
* Fixing README formatting.
2010-02-27 Doug Fales <doug@falesafeconsulting.com>
* README edits.
* More rdoc tweaks.
* Changing README to rdoc ext for github.
2009-10-13 Doug Fales <doug@falesafeconsulting.com>
* Adding the ability to write GPX to a string in addition to a file. Thanks to Douglas Robertson for the patch.
2009-09-27 Doug Fales <doug@falesafeconsulting.com>
* Adding a patch from Douglas Robertson that allows using version 1.0 of the schema for output.
2009-07-07 Doug Fales <doug@falesafeconsulting.com>
* Adding changelog.
* Revving to version 0.5.
* Changing my contact email address.
* Patches from Tom Verbeure (mtbguru.com) to work with libxml-ruby 1.x.
2009-06-17 Doug Fales <doug@falesafeconsulting.com>
* Patch from Kang-min Liu to support speed element.
2008-02-19 Doug Fales <doug@falesafeconsulting.com>
* Revving to 0.4.
* 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.
2008-02-11 Doug Fales <doug@falesafeconsulting.com>
* Going to version 0.3.
* Updating unit tests in light of recent fixes to routes and waypoints code.
2008-02-08 Doug Fales <doug@falesafeconsulting.com>
* Thanks to Mike Gauland for discovering some route- and waypoint-related bugs. I've fixed them and also added #to_s on Waypoint so it's easier to debug.
2007-12-04 Doug Fales <doug@falesafeconsulting.com>
* Thanks to Christian Koerner for finding and fixing these bugs in the waypoint code.
* Another patch from Gaku Ueda. This one allows you to pass in a string of GPX data using the :gpx_date => option. Thanks Gaku!
2007-11-30 Doug Fales <doug@falesafeconsulting.com>
* Updating the version #.
* 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.
2006-12-04 Doug Fales <doug@falesafeconsulting.com>
* First stab at using libxml-ruby instead of REXML. I'm seeing the unit tests finish in under 14 seconds. That is compared to 2 minutes using REXML.
2006-12-03 Doug Fales <doug@falesafeconsulting.com>
* Fixing more nil time exceptions.
* Fixing an exception in contains_time?.
2006-11-28 Doug Fales <doug@falesafeconsulting.com>
* A couple of fixes to make the library comply with the different attribute names possible on the bounds element.
2006-10-28 Doug Fales <doug@falesafeconsulting.com>
* Fixing nil time bug.
2006-10-14 Doug Fales <doug@falesafeconsulting.com>
* Initial import of gpx gem.

View File

@ -1,4 +0,0 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in seryz.gemspec
gemspec

View File

@ -1,9 +1,8 @@
= GPX Gem = GPX Gem
Copyright (C) 2006 Doug Fales
Copyright (C) 2006 Doug Fales mailto:doug@falesafeconsulting.com Doug Fales mailto:doug.fales@gmail.com
== What It Does == What It Does
This library reads GPX files and provides an API for reading and manipulating This library reads GPX files and provides an API for reading and manipulating
the data as objects. For more info on the GPX format, see the data as objects. For more info on the GPX format, see
http://www.topografix.com/gpx.asp. http://www.topografix.com/gpx.asp.
@ -15,7 +14,6 @@ the tracks and points in a file (such as distance, duration, average speed,
etc). etc).
== Examples == Examples
Reading a GPX file, and cropping its contents to a given area: Reading a GPX file, and cropping its contents to a given area:
gpx = GPX::GPXFile.new(:gpx_file => filename) # Read GPX file gpx = GPX::GPXFile.new(:gpx_file => filename) # Read GPX file
bounds = GPX::Bounds.new(params) # Create a rectangular area to crop bounds = GPX::Bounds.new(params) # Create a rectangular area to crop
@ -29,14 +27,13 @@ Converting a Magellan track log to GPX:
== Notes == Notes
This library was written to bridge the gap between my Garmin Geko This library was written to bridge the gap between my Garmin Geko
and my website, WalkingBoss.org (RIP). For that reason, it has always been more of a and my website, WalkingBoss.org. For that reason, it has always been more of a
work-in-progress than an attempt at full GPX compliance. The track side of the work-in-progress than an attempt at full GPX compliance. The track side of the
library has seen much more use than the route/waypoint side, so if you're doing library has seen much more use than the route/waypoint side, so if you're doing
something with routes or waypoints, you may need to tweak some things. something with routes or waypoints, you may need to tweak some things.
Since this code uses XML to read an entire GPX file into memory, it is not Since this code uses REXML to read an entire GPX file into memory, it is not
the fastest possible solution for working with GPX data, especially if you are the fastest possible solution for working with GPX data, especially if you are
working with tracks from several days or weeks. working with tracks from several days or weeks.

View File

@ -1,6 +1,20 @@
require 'bundler/gem_tasks' require 'rubygems'
require 'rake'
require 'rake/testtask' require 'rake/testtask'
require 'rdoc/task' require 'rake/rdoctask'
require 'rake/gempackagetask'
require File.dirname(__FILE__) + '/lib/gpx'
PKG_VERSION = GPX::VERSION
PKG_NAME = "gpx"
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
RUBY_FORGE_PROJECT = "gpx"
RUBY_FORGE_USER = ENV['RUBY_FORGE_USER'] || "dougfales"
RELEASE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
PKG_FILES = FileList[
"lib/**/*", "bin/*", "tests/**/*", "[A-Z]*", "Rakefile", "doc/**/*"
]
desc "Default Task" desc "Default Task"
task :default => [ :test ] task :default => [ :test ]
@ -13,6 +27,12 @@ Rake::TestTask.new("test") { |t|
t.verbose = true t.verbose = true
} }
# Make a console, useful when working on tests
desc "Generate a test console"
task :console do
verbose( false ) { sh "irb -I lib/ -r 'gpx'" }
end
# Genereate the RDoc documentation # Genereate the RDoc documentation
desc "Create documentation" desc "Create documentation"
Rake::RDocTask.new("doc") { |rdoc| Rake::RDocTask.new("doc") { |rdoc|
@ -21,3 +41,51 @@ Rake::RDocTask.new("doc") { |rdoc|
rdoc.rdoc_files.include('README') rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb') rdoc.rdoc_files.include('lib/**/*.rb')
} }
# Genereate the package
spec = Gem::Specification.new do |s|
#### Basic information.
s.name = 'gpx'
s.version = PKG_VERSION
s.summary = <<-EOF
A basic API for reading and writing GPX files.
EOF
s.description = <<-EOF
A basic API for reading and writing GPX files.
EOF
#### Which files are to be included in this gem? Everything! (Except CVS directories.)
s.files = PKG_FILES
#### Load-time details: library and application (you will need one or both).
s.require_path = 'lib'
s.autorequire = 'gpx'
#### Documentation and testing.
s.has_rdoc = true
#### Author and project details.
s.author = "Doug Fales"
s.email = "doug.fales@gmail.com"
#s.homepage = "http://gpx.rubyforge.com/"
end
Rake::GemPackageTask.new(spec) do |pkg|
pkg.need_zip = true
pkg.need_tar = true
end
desc "Report code statistics (KLOCs, etc) from the application"
task :stats do
require 'code_statistics'
CodeStatistics.new(
["Library", "lib"],
["Units", "tests"]
).to_s
end

View File

@ -1,22 +0,0 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'gpx/version'
Gem::Specification.new do |s|
s.name = 'gpx'
s.version = GPX::VERSION
s.authors = ["Guillaume Dott", "Doug Fales"]
s.email = ["guillaume+github@dott.fr", "doug.fales@gmail.com"]
s.summary = %q{A basic API for reading and writing GPX files.}
s.description = %q{A basic API for reading and writing GPX files.}
s.files = `git ls-files`.split($/)
s.test_files = s.files.grep(%r{^(test|spec|features)/})
s.require_paths = ["lib"]
s.has_rdoc = true
s.homepage = "http://dougfales.github.com/gpx/"
s.rubyforge_project = "gpx"
s.add_dependency 'nokogiri'
end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -20,12 +20,11 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
$:.unshift(File.dirname(__FILE__))
require 'rexml/document'
require 'date'
require 'time' require 'time'
require 'nokogiri' require 'csv'
require 'gpx/version'
require 'gpx/gpx' require 'gpx/gpx'
require 'gpx/gpx_file' require 'gpx/gpx_file'
require 'gpx/bounds' require 'gpx/bounds'

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,54 +21,63 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
class Bounds < Base class Bounds < Base
attr_accessor :min_lat, :max_lat, :max_lon, :min_lon, :center_lat, :center_lon attr_accessor :min_lat, :max_lat, :max_lon, :min_lon, :center_lat, :center_lon
# Creates a new bounds object with the passed-in min and max longitudes # Creates a new bounds object with the passed-in min and max longitudes
# and latitudes. # and latitudes.
def initialize(opts = { :min_lat => 90.0, :max_lat => -90.0, :min_lon => 180.0, :max_lon => -180.0}) def initialize(opts = { :min_lat => 90.0, :max_lat => -90.0, :min_lon => 180.0, :max_lon => -180.0})
@min_lat, @max_lat = opts[:min_lat].to_f, opts[:max_lat].to_f @min_lat, @max_lat = opts[:min_lat].to_f, opts[:max_lat].to_f
@min_lon, @max_lon = opts[:min_lon].to_f, opts[:max_lon].to_f @min_lon, @max_lon = opts[:min_lon].to_f, opts[:max_lon].to_f
end
# Returns the middle latitude.
def center_lat
distance = (max_lat - min_lat)/2.0
(min_lat + distance)
end
# Returns the middle longitude.
def center_lon
distance = (max_lon - min_lon)/2.0
(min_lon + distance)
end
# Returns true if the pt is within these bounds.
def contains?(pt)
(pt.lat >= min_lat and pt.lat <= max_lat and pt.lon >= min_lon and pt.lon <= max_lon)
end
# Adds an item to itself, expanding its min/max lat/lon as needed to
# contain the given item. The item can be either another instance of
# Bounds or a Point.
def add(item)
if(item.respond_to?(:lat) and item.respond_to?(:lon))
@min_lat = item.lat if item.lat < @min_lat
@min_lon = item.lon if item.lon < @min_lon
@max_lat = item.lat if item.lat > @max_lat
@max_lon = item.lon if item.lon > @max_lon
else
@min_lat = item.min_lat if item.min_lat < @min_lat
@min_lon = item.min_lon if item.min_lon < @min_lon
@max_lat = item.max_lat if item.max_lat > @max_lat
@max_lon = item.max_lon if item.max_lon > @max_lon
end end
end
# Returns the min_lat, min_lon, max_lat, and max_lon in a labeled string. # Returns the middle latitude.
def to_s def center_lat
"min_lat: #{min_lat} min_lon: #{min_lon} max_lat: #{max_lat} max_lon: #{max_lon}" distance = (max_lat - min_lat)/2.0
end (min_lat + distance)
end
end # Returns the middle longitude.
def center_lon
distance = (max_lon - min_lon)/2.0
(min_lon + distance)
end
def to_xml
bnd = REXML::Element.new('bounds')
bnd.attributes['min_lat'] = min_lat
bnd.attributes['min_lon'] = min_lon
bnd.attributes['max_lat'] = max_lat
bnd.attributes['max_lon'] = max_lon
bnd
end
# Returns true if the pt is within these bounds.
def contains?(pt)
(pt.lat >= min_lat and pt.lat <= max_lat and pt.lon >= min_lon and pt.lon <= max_lon)
end
# Adds an item to itself, expanding its min/max lat/lon as needed to
# contain the given item. The item can be either another instance of
# Bounds or a Point.
def add(item)
if(item.respond_to?(:lat) and item.respond_to?(:lon))
@min_lat = item.lat if item.lat < @min_lat
@min_lon = item.lon if item.lon < @min_lon
@max_lat = item.lat if item.lat > @max_lat
@max_lon = item.lon if item.lon > @max_lon
else
@min_lat = item.min_lat if item.min_lat < @min_lat
@min_lon = item.min_lon if item.min_lon < @min_lon
@max_lat = item.max_lat if item.max_lat > @max_lat
@max_lon = item.max_lon if item.max_lon > @max_lon
end
end
# Returns the min_lat, min_lon, max_lat, and max_lon in a labeled string.
def to_s
"min_lat: #{min_lat} min_lon: #{min_lon} max_lat: #{max_lat} max_lon: #{max_lon}"
end
end
end end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,26 +21,33 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
# A common base class which provides a useful initializer method to many VERSION = "0.1"
# class in the GPX library.
class Base # A common base class which provides a useful initializer method to many
# This initializer can take an XML::Node and scrape out any text # class in the GPX library.
# elements with the names given in the "text_elements" array. Each class Base
# element found underneath "parent" with a name in "text_elements" causes include REXML
# 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 # This initializer can take a REXML::Element and scrape out any text
# class (Route, TrackPoint, Track, etc). Just pass an array of possible # elements with the names given in the "text_elements" array. Each
# attributes to this method. # element found underneath "parent" with a name in "text_elements" causes
def instantiate_with_text_elements(parent, text_elements) # an attribute to be initialized on the instance. This means you don't
text_elements.each do |el| # have to pick out individual text elements in each initializer of each
child_xpath = "#{el}" # class (Route, TrackPoint, Track, etc). Just pass an array of possible
unless parent.at(child_xpath).nil? # attributes to this method.
val = parent.at(child_xpath).inner_text def instantiate_with_text_elements(parent, text_elements)
self.send("#{el}=", val) text_elements.each do |el|
end unless parent.elements[el].nil?
val = parent.elements[el].text
code = <<-code
attr_accessor #{ el }
#{el}=#{val}
code
class_eval code
end
end
end end
end end
end
end end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,268 +21,202 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
class GPXFile < Base class GPXFile < Base
attr_accessor :tracks, :routes, :waypoints, :bounds, :lowest_point, :highest_point, :duration, :ns, :time, :name attr_reader :tracks, :routes, :waypoints, :bounds, :lowest_point, :highest_point, :distance, :duration, :average_speed
# This initializer can be used to create a new GPXFile from an existing
# file or to create a new GPXFile instance with no data (so that you can
# add tracks and points and write it out to a new file later).
# To read an existing GPX file, do this:
# gpx_file = GPXFile.new(:gpx_file => 'mygpxfile.gpx')
# puts "Speed: #{gpx_file.average_speed}"
# puts "Duration: #{gpx_file.duration}"
# puts "Bounds: #{gpx_file.bounds}"
#
# To read a GPX file from a string, use :gpx_data.
# gpx_file = GPXFile.new(:gpx_data => '<xml ...><gpx>...</gpx>)
# To create a new blank GPXFile instance:
# gpx_file = GPXFile.new
# Note that you can pass in any instance variables to this form of the initializer, including Tracks or Segments:
# some_track = get_track_from_csv('some_other_format.csv')
# gpx_file = GPXFile.new(:tracks => [some_track])
#
def initialize(opts = {})
@duration = 0
if(opts[:gpx_file] or opts[:gpx_data])
if opts[:gpx_file]
gpx_file = opts[:gpx_file]
gpx_file = File.open(gpx_file) unless gpx_file.is_a?(File)
@xml = Nokogiri::XML(gpx_file)
else
@xml = Nokogiri::XML(opts[:gpx_data])
end
reset_meta_data # This initializer can be used to create a new GPXFile from an existing
bounds_element = (@xml.at("metadata/bounds") rescue nil) # file or to create a new GPXFile instance with no data (so that you can
if bounds_element # add tracks and points and write it out to a new file later).
@bounds.min_lat = get_bounds_attr_value(bounds_element, %w{ min_lat minlat minLat }) # To read an existing GPX file, do this:
@bounds.min_lon = get_bounds_attr_value(bounds_element, %w{ min_lon minlon minLon}) # gpx_file = GPXFile.new(:gpx_file => 'mygpxfile.gpx')
@bounds.max_lat = get_bounds_attr_value(bounds_element, %w{ max_lat maxlat maxLat}) # puts "Speed: #{gpx_file.average_speed}"
@bounds.max_lon = get_bounds_attr_value(bounds_element, %w{ max_lon maxlon maxLon}) # puts "Duration: #{gpx_file.duration}"
else # puts "Bounds: #{gpx_file.bounds}"
get_bounds = true #
end # To create a new blank GPXFile instance:
# gpx_file = GPXFile.new
# Note that you can pass in any instance variables to this form of the initializer, including Tracks or Segments:
# some_track = get_track_from_csv('some_other_format.csv')
# gpx_file = GPXFile.new(:tracks => [some_track])
#
def initialize(opts = {})
@duration = 0
if(opts[:gpx_file])
gpx_file = opts[:gpx_file]
case gpx_file
when String
gpx_file = File.open(gpx_file)
end
reset_meta_data
@xml = Document.new(gpx_file, :ignore_whitespace_nodes => :all)
@time = Time.parse(@xml.at("metadata/time").inner_text) rescue nil bounds_element = (XPath.match(@xml, "/gpx/metadata/bounds").first rescue nil)
@name = @xml.at("metadata/name").inner_text rescue nil if bounds_element
@tracks = [] @bounds.min_lat = bounds_element.attributes["min_lat"].to_f
@xml.search("trk").each do |trk| @bounds.min_lon = bounds_element.attributes["min_lon"].to_f
trk = Track.new(:element => trk, :gpx_file => self) @bounds.max_lat = bounds_element.attributes["max_lat"].to_f
update_meta_data(trk, get_bounds) @bounds.max_lon = bounds_element.attributes["max_lon"].to_f
@tracks << trk else
end get_bounds = true
@waypoints = [] end
@xml.search("wpt").each { |wpt| @waypoints << Waypoint.new(:element => wpt, :gpx_file => self) }
@routes = []
@xml.search("rte").each { |rte| @routes << Route.new(:element => rte, :gpx_file => self) }
@tracks.delete_if { |t| t.empty? }
calculate_duration @tracks = XPath.match(@xml, "/gpx/trk").collect do |trk|
else trk = Track.new(:element => trk, :gpx_file => self)
reset_meta_data update_meta_data(trk, get_bounds)
opts.each { |attr_name, value| instance_variable_set("@#{attr_name.to_s}", value) } trk
unless(@tracks.nil? or @tracks.size.zero?) end
@tracks.each { |trk| update_meta_data(trk) } @waypoints = XPath.match(@xml, "/gpx/wpt").collect { |wpt| Waypoint.new(:element => wpt, :gpx_file => self) }
calculate_duration @routes = XPath.match(@xml, "/gpx/rte").collect { |rte| Route.new(:element => rte, :gpx_file => self) }
end
end
@tracks ||= []
@routes ||= []
@waypoints ||= []
end
def get_bounds_attr_value(el, possible_names) @tracks.delete_if { |t| t.empty? }
result = nil
possible_names.each do |name|
result = el[name]
break unless result.nil?
end
return (result.to_f rescue nil)
end
# Returns the distance, in kilometers, meters, or miles, of all of the calculate_duration
# tracks and segments contained in this GPXFile. else
def distance(opts = { :units => 'kilometers' }) reset_meta_data
case opts[:units] opts.each { |attr_name, value| instance_variable_set("@#{attr_name.to_s}", value) }
when /kilometers/i unless(@tracks.nil? or @tracks.size.zero?)
return @distance @tracks.each { |trk| update_meta_data(trk) }
when /meters/i calculate_duration
return (@distance * 1000) end
when /miles/i end
return (@distance * 0.62)
end
end
# Returns the average speed, in km/hr, meters/hr, or miles/hr, of this
# GPXFile. The calculation is based on the total distance divided by the
# total duration of the entire file.
def average_speed(opts = { :units => 'kilometers' })
case opts[:units]
when /kilometers/i
return @distance / (@duration/3600.0)
when /meters/i
return (@distance * 1000) / (@duration/3600.0)
when /miles/i
return (@distance * 0.62) / (@duration/3600.0)
end
end
# Crops any points falling within a rectangular area. Identical to the
# delete_area method in every respect except that the points outside of
# the given area are deleted. Note that this method automatically causes
# the meta data to be updated after deletion.
def crop(area)
reset_meta_data
keep_tracks = []
tracks.each do |trk|
trk.crop(area)
unless trk.empty?
update_meta_data(trk)
keep_tracks << trk
end
end
@tracks = keep_tracks
routes.each { |rte| rte.crop(area) }
waypoints.each { |wpt| wpt.crop(area) }
end
# Deletes any points falling within a rectangular area. The "area"
# parameter is usually an instance of the Bounds class. Note that this
# method cascades into similarly named methods of subordinate classes
# (i.e. Track, Segment), which means, if you want the deletion to apply
# to all the data, you only call this one (and not the one in Track or
# Segment classes). Note that this method automatically causes the meta
# data to be updated after deletion.
def delete_area(area)
reset_meta_data
keep_tracks = []
tracks.each do |trk|
trk.delete_area(area)
unless trk.empty?
update_meta_data(trk)
keep_tracks << trk
end
end
@tracks = keep_tracks
routes.each { |rte| rte.delete_area(area) }
waypoints.each { |wpt| wpt.delete_area(area) }
end
# Resets the meta data for this GPX file. Meta data includes the bounds,
# the high and low points, and the distance.
def reset_meta_data
@bounds = Bounds.new
@highest_point = nil
@lowest_point = nil
@distance = 0.0
end
# Updates the meta data for this GPX file. Meta data includes the
# bounds, the high and low points, and the distance. This is useful when
# you modify the GPX data (i.e. by adding or deleting points) and you
# want the meta data to accurately reflect the new data.
def update_meta_data(trk, get_bounds = true)
@lowest_point = trk.lowest_point if(@lowest_point.nil? or (!trk.lowest_point.nil? and trk.lowest_point.elevation < @lowest_point.elevation))
@highest_point = trk.highest_point if(@highest_point.nil? or (!trk.highest_point.nil? and trk.highest_point.elevation > @highest_point.elevation))
@bounds.add(trk.bounds) if get_bounds
@distance += trk.distance
end
# 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, update_time = true)
@time = Time.now if(@time.nil? or update_time)
@name ||= File.basename(filename)
doc = generate_xml_doc
File.open(filename, 'w') { |f| f.write(doc.to_xml) }
end
def to_s(update_time = true)
@time = Time.now if(@time.nil? or update_time)
doc = generate_xml_doc
doc.to_xml
end
def inspect
"<#{self.class.name}:...>"
end
private
def generate_xml_doc
version = '1.1'
version_dir = version.gsub('.','/')
doc = Nokogiri::XML::Builder.new do |xml|
xml.gpx(
'xsi' => "http://www.w3.org/2001/XMLSchema-instance",
'version' => version.to_s,
'creator' => "GPX RubyGem #{GPX::VERSION}",
'xsi:schemaLocation' => "http://www.topografix.com/GPX/#{version_dir} http://www.topografix.com/GPX/#{version_dir}/gpx.xsd") \
{
xml.metadata {
xml.name @name
xml.time @time.xmlschema
xml.bound(
minlat: bounds.min_lat,
minlon: bounds.min_lon,
maxlat: bounds.max_lat,
maxlon: bounds.max_lon,
)
}
tracks.each do |t|
xml.trk {
xml.name t.name
t.segments.each do |seg|
xml.trkseg {
seg.points.each do |p|
xml.trkpt(lat: p.lat, lon: p.lon) {
xml.time p.time.xmlschema unless p.time.nil?
xml.ele p.elevation unless p.elevation.nil?
}
end
}
end
}
end unless tracks.nil?
waypoints.each do |w|
xml.wpt(lat: w.lat, lon: w.lon) {
Waypoint::SUB_ELEMENTS.each do |sub_elem|
xml.send(sub_elem, w.send(sub_elem)) if w.respond_to?(sub_elem) && !w.send(sub_elem).nil?
end
}
end unless waypoints.nil?
routes.each do |r|
xml.rte {
xml.name r.name
r.points.each do |p|
xml.rtept(lat: p.lat, lon: p.lon) {
xml.time p.time.xmlschema unless p.time.nil?
xml.ele p.elevation unless p.elevation.nil?
}
end
}
end unless routes.nil?
}
end end
return doc # Returns the distance, in kilometers, meters, or miles, of all of the
end # tracks and segments contained in this GPXFile.
def distance(opts = { :units => 'kilometers' })
# Calculates and sets the duration attribute by subtracting the time on case opts[:units]
# the very first point from the time on the very last point. when /kilometers/i
def calculate_duration return @distance
@duration = 0 when /meters/i
if(@tracks.nil? or @tracks.size.zero? or @tracks[0].segments.nil? or @tracks[0].segments.size.zero?) return (@distance * 1000)
return @duration when /miles/i
return (@distance * 0.62)
end
end end
@duration = (@tracks[-1].segments[-1].points[-1].time - @tracks.first.segments.first.points.first.time)
rescue # Returns the average speed, in km/hr, meters/hr, or miles/hr, of this
@duration = 0 # GPXFile. The calculation is based on the total distance divided by the
end # total duration of the entire file.
end def average_speed(opts = { :units => 'kilometers' })
case opts[:units]
when /kilometers/i
return @distance / (@duration/3600.0)
when /meters/i
return (@distance * 1000) / (@duration/3600.0)
when /miles/i
return (@distance * 0.62) / (@duration/3600.0)
end
end
# Crops any points falling within a rectangular area. Identical to the
# delete_area method in every respect except that the points outside of
# the given area are deleted. Note that this method automatically causes
# the meta data to be updated after deletion.
def crop(area)
reset_meta_data
keep_tracks = []
tracks.each do |trk|
trk.crop(area)
unless trk.empty?
update_meta_data(trk)
keep_tracks << trk
end
end
@tracks = keep_tracks
routes.each { |rte| rte.crop(area) }
waypoints.each { |wpt| wpt.crop(area) }
end
# Deletes any points falling within a rectangular area. The "area"
# parameter is usually an instance of the Bounds class. Note that this
# method cascades into similarly named methods of subordinate classes
# (i.e. Track, Segment), which means, if you want the deletion to apply
# to all the data, you only call this one (and not the one in Track or
# Segment classes). Note that this method automatically causes the meta
# data to be updated after deletion.
def delete_area(area)
reset_meta_data
keep_tracks = []
tracks.each do |trk|
trk.delete_area(area)
unless trk.empty?
update_meta_data(trk)
keep_tracks << trk
end
end
@tracks = keep_tracks
routes.each { |rte| rte.delete_area(area) }
waypoints.each { |wpt| wpt.delete_area(area) }
end
# Resets the meta data for this GPX file. Meta data includes the bounds,
# the high and low points, and the distance.
def reset_meta_data
@bounds = Bounds.new
@highest_point = nil
@lowest_point = nil
@distance = 0.0
end
# Updates the meta data for this GPX file. Meta data includes the
# bounds, the high and low points, and the distance. This is useful when
# you modify the GPX data (i.e. by adding or deleting points) and you
# want the meta data to accurately reflect the new data.
def update_meta_data(trk, get_bounds = true)
@lowest_point = trk.lowest_point if(@lowest_point.nil? or trk.lowest_point.elevation < @lowest_point.elevation)
@highest_point = trk.highest_point if(@highest_point.nil? or trk.highest_point.elevation > @highest_point.elevation)
@bounds.add(trk.bounds) if get_bounds
@distance += trk.distance
end
# 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)
doc = Document.new
gpx_elem = Element.new('gpx')
doc.add(gpx_elem)
gpx_elem.attributes['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
gpx_elem.attributes['xmlns'] = "http://www.topografix.com/GPX/1/1"
gpx_elem.attributes['version'] = "1.1"
gpx_elem.attributes['creator'] = "GPX RubyGem 0.1 Copyright 2006 Doug Fales -- http://walkingboss.com"
gpx_elem.attributes['xsi:schemaLocation'] = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
meta_data_elem = Element.new('metadata')
name_elem = Element.new('name')
name_elem.text = File.basename(filename)
meta_data_elem.elements << name_elem
time_elem = Element.new('time')
time_elem.text = Time.now.xmlschema
meta_data_elem.elements << time_elem
meta_data_elem.elements << bounds.to_xml
gpx_elem.elements << meta_data_elem
tracks.each { |t| gpx_elem.add_element t.to_xml } unless tracks.nil?
waypoints.each { |w| gpx_elem.add_element w.to_xml } unless waypoints.nil?
routes.each { |r| gpx_elem.add_element r.to_xml } unless routes.nil?
File.open(filename, 'w') { |f| doc.write(f) }
end
private
# Calculates and sets the duration attribute by subtracting the time on
# the very first point from the time on the very last point.
def calculate_duration
@duration = 0
if(@tracks.nil? or @tracks.size.zero? or @tracks[0].segments.nil? or @tracks[0].segments.size.zero?)
return @duration
end
@duration = (@tracks[-1].segments[-1].points[-1].time - @tracks.first.segments.first.points.first.time)
rescue
@duration = 0
end
end
end end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,112 +21,114 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
# This class will parse the lat/lon and time data from a Magellan track log,
# which is a NMEA formatted CSV list of points.
class MagellanTrackLog
#PMGNTRK
# This message is to be used to transmit Track information (basically a list of previous position fixes)
# which is often displayed on the plotter or map screen of the unit. The first field in this message
# is the Latitude, followed by N or S. The next field is the Longitude followed by E or W. The next
# field is the altitude followed by “F” for feet or “M” for meters. The next field is
# the UTC time of the fix. The next field consists of a status letter of “A” to indicated that
# the data is valid, or “V” to indicate that the data is not valid. The last character field is
# the name of the track, for those units that support named tracks. The last field contains the UTC date
# of the fix. Note that this field is (and its preceding comma) is only produced by the unit when the
# command PMGNCMD,TRACK,2 is given. It is not present when a simple command of PMGNCMD,TRACK is issued.
#NOTE: The Latitude and Longitude Fields are shown as having two decimal # This class will parse the lat/lon and time data from a Magellan track log,
# places. As many additional decimal places may be added as long as the total # which is a NMEA formatted CSV list of points.
# length of the message does not exceed 82 bytes.
# $PMGNTRK,llll.ll,a,yyyyy.yy,a,xxxxx,a,hhmmss.ss,A,c----c,ddmmyy*hh<CR><LF> class MagellanTrackLog
require 'csv' #PMGNTRK
# This message is to be used to transmit Track information (basically a list of previous position fixes)
# which is often displayed on the plotter or map screen of the unit. The first field in this message
# is the Latitude, followed by N or S. The next field is the Longitude followed by E or W. The next
# field is the altitude followed by “F” for feet or “M” for meters. The next field is
# the UTC time of the fix. The next field consists of a status letter of “A” to indicated that
# the data is valid, or “V” to indicate that the data is not valid. The last character field is
# the name of the track, for those units that support named tracks. The last field contains the UTC date
# of the fix. Note that this field is (and its preceding comma) is only produced by the unit when the
# command PMGNCMD,TRACK,2 is given. It is not present when a simple command of PMGNCMD,TRACK is issued.
LAT = 1 #NOTE: The Latitude and Longitude Fields are shown as having two decimal
LAT_HEMI = 2 # places. As many additional decimal places may be added as long as the total
LON = 3 # length of the message does not exceed 82 bytes.
LON_HEMI = 4
ELE = 5
ELE_UNITS = 6
TIME = 7
INVALID_FLAG = 8
DATE = 10
FEET_TO_METERS = 0.3048 # $PMGNTRK,llll.ll,a,yyyyy.yy,a,xxxxx,a,hhmmss.ss,A,c----c,ddmmyy*hh<CR><LF>
require 'csv'
class << self LAT = 1
LAT_HEMI = 2
LON = 3
LON_HEMI = 4
ELE = 5
ELE_UNITS = 6
TIME = 7
INVALID_FLAG = 8
DATE = 10
# Takes the name of a magellan file, converts the contents to GPX, and FEET_TO_METERS = 0.3048
# writes the result to gpx_filename.
def convert_to_gpx(magellan_filename, gpx_filename)
segment = Segment.new class << self
CSV.open(magellan_filename, "r").each do |row| # Takes the name of a magellan file, converts the contents to GPX, and
next if(row.size < 10 or row[INVALID_FLAG] == 'V') # writes the result to gpx_filename.
def convert_to_gpx(magellan_filename, gpx_filename)
lat_deg = row[LAT][0..1] segment = Segment.new
lat_min = row[LAT][2...-1]
lat_hemi = row[LAT_HEMI]
lat = lat_deg.to_f + (lat_min.to_f / 60.0) CSV.open(magellan_filename, "r") do |row|
lat = (-lat) if(lat_hemi == 'S') next if(row.size < 10 or row[INVALID_FLAG] == 'V')
lon_deg = row[LON][0..2] lat_deg = row[LAT][0..1]
lon_min = row[LON][3..-1] lat_min = row[LAT][2...-1]
lon_hemi = row[LON_HEMI] lat_hemi = row[LAT_HEMI]
lon = lon_deg.to_f + (lon_min.to_f / 60.0) lat = lat_deg.to_f + (lat_min.to_f / 60.0)
lon = (-lon) if(lon_hemi == 'W') lat = (-lat) if(lat_hemi == 'S')
lon_deg = row[LON][0..2]
lon_min = row[LON][3..-1]
lon_hemi = row[LON_HEMI]
lon = lon_deg.to_f + (lon_min.to_f / 60.0)
lon = (-lon) if(lon_hemi == 'W')
ele = row[ELE] ele = row[ELE]
ele_units = row[ELE_UNITS] ele_units = row[ELE_UNITS]
ele = ele.to_f ele = ele.to_f
if(ele_units == 'F') if(ele_units == 'F')
ele *= FEET_TO_METERS ele *= FEET_TO_METERS
end end
hrs = row[TIME][0..1].to_i hrs = row[TIME][0..1].to_i
mins = row[TIME][2..3].to_i mins = row[TIME][2..3].to_i
secs = row[TIME][4..5].to_i secs = row[TIME][4..5].to_i
day = row[DATE][0..1].to_i day = row[DATE][0..1].to_i
mon = row[DATE][2..3].to_i mon = row[DATE][2..3].to_i
yr = 2000 + row[DATE][4..5].to_i yr = 2000 + row[DATE][4..5].to_i
time = Time.gm(yr, mon, day, hrs, mins, secs) time = Time.gm(yr, mon, day, hrs, mins, secs)
#must create point #must create point
pt = TrackPoint.new(:lat => lat, :lon => lon, :time => time, :elevation => ele) pt = TrackPoint.new(:lat => lat, :lon => lon, :time => time, :elevation => ele)
segment.append_point(pt) segment.append_point(pt)
end end
trk = Track.new trk = Track.new
trk.append_segment(segment) trk.append_segment(segment)
gpx_file = GPXFile.new(:tracks => [trk]) gpx_file = GPXFile.new(:tracks => [trk])
gpx_file.write(gpx_filename) gpx_file.write(gpx_filename)
end end
# Tests to see if the given file is a magellan NMEA track log. # Tests to see if the given file is a magellan NMEA track log.
def is_magellan_file?(filename) def is_magellan_file?(filename)
i = 0 i = 0
File.open(filename, "r") do |f| File.open(filename, "r") do |f|
f.each do |line| f.each do |line|
i += 1 i += 1
if line =~ /^\$PMGNTRK/ if line =~ /^\$PMGNTRK/
return true return true
elsif line =~ /<\?xml/ elsif line =~ /<\?xml/
return false return false
elsif(i > 10) elsif(i > 10)
return false return false
end
end
end end
end return false
end end
return false
end end
end
end end
end end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -20,71 +20,84 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
include Math
module GPX module GPX
# The base class for all points. Trackpoint and Waypoint both descend from this base class. # The base class for all points. Trackpoint and Waypoint both descend from this base class.
class Point < Base class Point < Base
D_TO_R = Math::PI/180.0; D_TO_R = PI/180.0;
attr_accessor :lat, :lon, :time, :elevation, :gpx_file, :speed attr_accessor :lat, :lon, :time, :elevation
# When you need to manipulate individual points, you can create a Point
# object with a latitude, a longitude, an elevation, and a time. In
# addition, you can pass a REXML 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 } )
if (opts[:element])
elem = opts[:element]
@lat, @lon = elem.attributes["lat"].to_f, elem.attributes["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.elements["time"].text) rescue nil)
@elevation = elem.elements["ele"].text.to_f if elem.elements["ele"]
else
@lat = opts[:lat]
@lon = opts[:lon]
@elevation = opts[:elevation]
@time = opts[:time]
end
# When you need to manipulate individual points, you can create a Point
# object with a latitude, a longitude, an elevation, and a time. In
# 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.at("time").inner_text) rescue nil)
@elevation = elem.at("ele").inner_text.to_f unless elem.at("ele").nil?
@speed = elem.at("speed").inner_text.to_f unless elem.at("speed").nil?
else
@lat = opts[:lat]
@lon = opts[:lon]
@elevation = opts[:elevation]
@time = opts[:time]
@speed = opts[:speed]
end end
end
# Returns the latitude and longitude (in that order), separated by the
# given delimeter. This is useful for passing a point into another API
# (i.e. the Google Maps javascript API).
def lat_lon(delim = ', ')
"#{lat}#{delim}#{lon}"
end
# Returns the latitude and longitude (in that order), separated by the # Returns the longitude and latitude (in that order), separated by the
# given delimeter. This is useful for passing a point into another API # given delimeter. This is useful for passing a point into another API
# (i.e. the Google Maps javascript API). # (i.e. the Google Maps javascript API).
def lat_lon(delim = ', ') def lon_lat(delim = ', ')
"#{lat}#{delim}#{lon}" "#{lon}#{delim}#{lat}"
end end
# Returns the longitude and latitude (in that order), separated by the # Latitude in radians.
# given delimeter. This is useful for passing a point into another API def latr
# (i.e. the Google Maps javascript API). @latr ||= (@lat * D_TO_R)
def lon_lat(delim = ', ') end
"#{lon}#{delim}#{lat}"
end
# Latitude in radians. # Longitude in radians.
def latr def lonr
@latr ||= (@lat * D_TO_R) @lonr ||= (@lon * D_TO_R)
end end
# Longitude in radians. # Set the latitude (in degrees).
def lonr def lat=(latitude)
@lonr ||= (@lon * D_TO_R) @latr = (latitude * D_TO_R)
end @lat = latitude
end
# Set the latitude (in degrees). # Set the longitude (in degrees).
def lat=(latitude) def lon=(longitude)
@latr = (latitude * D_TO_R) @lonr = (longitude * D_TO_R)
@lat = latitude @lon = longitude
end end
# Set the longitude (in degrees). # Convert this point to a REXML::Element.
def lon=(longitude) def to_xml(elem_name = 'trkpt')
@lonr = (longitude * D_TO_R) pt = Element.new('trkpt')
@lon = longitude pt.attributes['lat'] = lat
end pt.attributes['lon'] = lon
end time_elem = Element.new('time')
time_elem.text = time.xmlschema
pt.elements << time_elem
elev = Element.new('ele')
elev.text = elevation
pt.elements << elev
pt
end
end
end end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,38 +21,45 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
# A Route in GPX is very similar to a Track, but it is created by a user
# from a series of Waypoints, whereas a Track is created by the GPS device
# automatically logging your progress at regular intervals.
class Route < Base
attr_accessor :points, :name, :gpx_file # A Route in GPX is very similar to a Track, but it is created by a user
# from a series of Waypoints, whereas a Track is created by the GPS device
# automatically logging your progress at regular intervals.
class Route < Base
attr_reader :points, :name, :gpx_file
# Initialize a Route from a REXML::Element.
def initialize(opts = {})
rte_element = opts[:element]
@gpx_file = opts[:gpx_file]
@name = rte_element.elements["child::name"].text
@points = []
XPath.each(rte_element, "child::rtept") do |point|
@points << Point.new(:element => point)
end
# 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.at("name").inner_text
@points = []
rte_element.search("rtept").each do |point|
@points << Point.new(:element => point, :gpx_file => @gpx_file)
end
else
@points = (opts[:points] or [])
@name = (opts[:name])
end end
end # Delete points outside of a given area.
def crop(area)
points.delete_if{ |pt| not area.contains? pt }
end
# Delete points outside of a given area. # Delete points within the given area.
def crop(area) def delete_area(area)
points.delete_if{ |pt| not area.contains? pt } points.delete_if{ |pt| area.contains? pt }
end end
# Delete points within the given area. # Convert this Route to a REXML::Element.
def delete_area(area) def to_xml
points.delete_if{ |pt| area.contains? pt } rte = Element.new('rte')
end name_elem = Element.new('name')
end name_elem.text = name
rte.elements << name_elem
points.each { |rte_pt| rte.elements << rte_pt.to_xml('rtept') }
rte
end
end
end end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,194 +21,197 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
# A segment is the basic container in a GPX file. A Segment contains points
# (in this lib, they're called TrackPoints). A Track contains Segments. An
# instance of Segment knows its highest point, lowest point, earliest and
# latest points, distance, and bounds.
class Segment < Base
attr_reader :earliest_point, :latest_point, :bounds, :highest_point, :lowest_point, :distance # A segment is the basic container in a GPX file. A Segment contains points
attr_accessor :points, :track # (in this lib, they're called TrackPoints). A Track contains Segments. An
# instance of Segment knows its highest point, lowest point, earliest and
# latest points, distance, and bounds.
class Segment < Base
# If a XML::Node object is passed-in, this will initialize a new attr_reader :earliest_point, :latest_point, :bounds, :highest_point, :lowest_point, :distance
# Segment based on its contents. Otherwise, a blank Segment is created. attr_accessor :points, :track
def initialize(opts = {})
@gpx_file = opts[:gpx_file] # If a REXML::Element object is passed-in, this will initialize a new
@track = opts[:track] # Segment based on its contents. Otherwise, a blank Segment is created.
@points = [] def initialize(opts = {})
@earliest_point = nil @track = opts[:track]
@latest_point = nil @points = []
@highest_point = nil @earliest_point = nil
@lowest_point = nil @latest_point = nil
@distance = 0.0 @highest_point = nil
@bounds = Bounds.new @lowest_point = nil
if(opts[:element]) @distance = 0.0
segment_element = opts[:element] @bounds = Bounds.new
last_pt = nil if(opts[:element])
if segment_element.is_a?(Nokogiri::XML::Node) segment_element = opts[:element]
segment_element.search("trkpt").each do |trkpt| last_pt = nil
pt = TrackPoint.new(:element => trkpt, :segment => self, :gpx_file => @gpx_file) unless segment_element.is_a?(Text)
unless pt.time.nil? XPath.each(segment_element, "child::trkpt") do |trkpt|
@earliest_point = pt if(@earliest_point.nil? or pt.time < @earliest_point.time) pt = TrackPoint.new(:element => trkpt, :segment => self)
@latest_point = pt if(@latest_point.nil? or pt.time > @latest_point.time) @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)
unless pt.elevation.nil?
@lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
@highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
end
@bounds.min_lat = pt.lat if pt.lat < @bounds.min_lat
@bounds.min_lon = pt.lon if pt.lon < @bounds.min_lon
@bounds.max_lat = pt.lat if pt.lat > @bounds.max_lat
@bounds.max_lon = pt.lon if pt.lon > @bounds.max_lon
@distance += haversine_distance(last_pt, pt) unless last_pt.nil?
@points << pt
last_pt = pt
end
end end
unless pt.elevation.nil? end
@lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation) end
@highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
# Tack on a point to this Segment. All meta-data will be updated.
def append_point(pt)
last_pt = @points[-1]
@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)
@lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
@highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
@bounds.min_lat = pt.lat if pt.lat < @bounds.min_lat
@bounds.min_lon = pt.lon if pt.lon < @bounds.min_lon
@bounds.max_lat = pt.lat if pt.lat > @bounds.max_lat
@bounds.max_lon = pt.lon if pt.lon > @bounds.max_lon
@distance += haversine_distance(last_pt, pt) unless last_pt.nil?
@points << pt
end
# Returns true if the given time is within this Segment.
def contains_time?(time)
(time >= @earliest_point.time and time <= @latest_point.time)
end
# Finds the closest point in time to the passed-in time argument. Useful
# for matching up time-based objects (photos, video, etc) with a
# geographic location.
def closest_point(time)
find_closest(points, time)
end
# Deletes all points within this Segment that lie outside of the given
# area (which should be a Bounds object).
def crop(area)
delete_if { |pt| not area.contains?(pt) }
end
# Deletes all points in this Segment that lie within the given area.
def delete_area(area)
delete_if{ |pt| area.contains?(pt) }
end
# A handy method that deletes points based on a block that is passed in.
# If the passed-in block returns true when given a point, then that point
# is deleted. For example:
# delete_if{ |pt| area.contains?(pt) }
def delete_if
reset_meta_data
keep_points = []
last_pt = nil
points.each do |pt|
unless yield(pt)
keep_points << pt
update_meta_data(pt, last_pt)
last_pt = pt
end end
@bounds.min_lat = pt.lat if pt.lat < @bounds.min_lat end
@bounds.min_lon = pt.lon if pt.lon < @bounds.min_lon @points = keep_points
@bounds.max_lat = pt.lat if pt.lat > @bounds.max_lat
@bounds.max_lon = pt.lon if pt.lon > @bounds.max_lon
@distance += haversine_distance(last_pt, pt) unless last_pt.nil?
@points << pt
last_pt = pt
end
end
end end
end
# Tack on a point to this Segment. All meta-data will be updated. # Returns true if this Segment has no points.
def append_point(pt) def empty?
last_pt = @points[-1] (points.nil? or (points.size == 0))
@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)
@lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
@highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
@bounds.min_lat = pt.lat if pt.lat < @bounds.min_lat
@bounds.min_lon = pt.lon if pt.lon < @bounds.min_lon
@bounds.max_lat = pt.lat if pt.lat > @bounds.max_lat
@bounds.max_lon = pt.lon if pt.lon > @bounds.max_lon
@distance += haversine_distance(last_pt, pt) unless last_pt.nil?
@points << pt
end
# Returns true if the given time is within this Segment.
def contains_time?(time)
(time >= @earliest_point.time and time <= @latest_point.time) rescue false
end
# Finds the closest point in time to the passed-in time argument. Useful
# for matching up time-based objects (photos, video, etc) with a
# geographic location.
def closest_point(time)
find_closest(points, time)
end
# Deletes all points within this Segment that lie outside of the given
# area (which should be a Bounds object).
def crop(area)
delete_if { |pt| not area.contains?(pt) }
end
# Deletes all points in this Segment that lie within the given area.
def delete_area(area)
delete_if{ |pt| area.contains?(pt) }
end
# A handy method that deletes points based on a block that is passed in.
# If the passed-in block returns true when given a point, then that point
# is deleted. For example:
# delete_if{ |pt| area.contains?(pt) }
def delete_if
reset_meta_data
keep_points = []
last_pt = nil
points.each do |pt|
unless yield(pt)
keep_points << pt
update_meta_data(pt, last_pt)
last_pt = pt
end
end end
@points = keep_points
end
# Returns true if this Segment has no points. # Converts this Segment to a REXML::Element object.
def empty? def to_xml
(points.nil? or (points.size == 0)) seg = Element.new('trkseg')
end points.each { |pt| seg.elements << pt.to_xml }
seg
# Prints out a nice summary of this Segment.
def to_s
result = "Track Segment\n"
result << "\tSize: #{points.size} points\n"
result << "\tDistance: #{distance} km\n"
result << "\tEarliest Point: #{earliest_point.time.to_s} \n"
result << "\tLatest Point: #{latest_point.time.to_s} \n"
result << "\tLowest Point: #{lowest_point.elevation} \n"
result << "\tHighest Point: #{highest_point.elevation}\n "
result << "\tBounds: #{bounds.to_s}"
result
end
protected
def find_closest(pts, time)
return pts.first if pts.size == 1
midpoint = pts.size/2
if pts.size == 2
diff_1 = pts[0].time - time
diff_2 = pts[1].time - time
return (diff_1 < diff_2 ? pts[0] : pts[1])
end end
if time >= pts[midpoint].time and time <= pts[midpoint+1].time
return pts[midpoint] # Prints out a nice summary of this Segment.
def to_s
elsif(time <= pts[midpoint].time) result = "Track Segment\n"
return find_closest(pts[0..midpoint], time) result << "\tSize: #{points.size} points\n"
else result << "\tDistance: #{distance} km\n"
return find_closest(pts[(midpoint+1)..-1], time) result << "\tEarliest Point: #{earliest_point.time.to_s} \n"
result << "\tLatest Point: #{latest_point.time.to_s} \n"
result << "\tLowest Point: #{lowest_point.elevation} \n"
result << "\tHighest Point: #{highest_point.elevation}\n "
result << "\tBounds: #{bounds.to_s}"
result
end end
end
RADIUS = 6371; # earth's mean radius in km protected
def find_closest(pts, time)
return pts.first if pts.size == 1
midpoint = pts.size/2
if pts.size == 2
diff_1 = pts[0].time - time
diff_2 = pts[1].time - time
return (diff_1 < diff_2 ? pts[0] : pts[1])
end
if time >= pts[midpoint].time and time <= pts[midpoint+1].time
# Calculate the Haversine distance between two points. This is the method return pts[midpoint]
# the library uses to calculate the cumulative distance of GPX files.
def haversine_distance(p1, p2)
d_lat = p2.latr - p1.latr;
d_lon = p2.lonr - p1.lonr;
a = Math.sin(d_lat/2) * Math.sin(d_lat/2) + Math.cos(p1.latr) * Math.cos(p2.latr) * Math.sin(d_lon/2) * Math.sin(d_lon/2);
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
d = RADIUS * c;
return d;
end
# Calculate the plain Pythagorean difference between two points. Not currently used. elsif(time <= pts[midpoint].time)
def pythagorean_distance(p1, p2) return find_closest(pts[0..midpoint], time)
Math.sqrt((p2.latr - p1.latr)**2 + (p2.lonr - p1.lonr)**2) else
end return find_closest(pts[(midpoint+1)..-1], time)
end
# Calculates the distance between two points using the Law of Cosines formula. Not currently used.
def law_of_cosines_distance(p1, p2)
(Math.acos(Math.sin(p1.latr)*Math.sin(p2.latr) + Math.cos(p1.latr)*Math.cos(p2.latr)*Math.cos(p2.lonr-p1.lonr)) * RADIUS)
end
def reset_meta_data
@earliest_point = nil
@latest_point = nil
@highest_point = nil
@lowest_point = nil
@distance = 0.0
@bounds = Bounds.new
end
def update_meta_data(pt, last_pt)
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)
end end
unless pt.elevation.nil?
@lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
@highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
end
@bounds.add(pt)
@distance += haversine_distance(last_pt, pt) unless last_pt.nil?
end
end RADIUS = 6371; # earth's mean radius in km
# Calculate the Haversine distance between two points. This is the method
# the library uses to calculate the cumulative distance of GPX files.
def haversine_distance(p1, p2)
d_lat = p2.latr - p1.latr;
d_lon = p2.lonr - p1.lonr;
a = Math.sin(d_lat/2) * Math.sin(d_lat/2) + Math.cos(p1.latr) * Math.cos(p2.latr) * Math.sin(d_lon/2) * Math.sin(d_lon/2);
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
d = RADIUS * c;
return d;
end
# Calculate the plain Pythagorean difference between two points. Not currently used.
def pythagorean_distance(p1, p2)
Math.sqrt((p2.latr - p1.latr)**2 + (p2.lonr - p1.lonr)**2)
end
# Calculates the distance between two points using the Law of Cosines formula. Not currently used.
def law_of_cosines_distance(p1, p2)
(Math.acos(Math.sin(p1.latr)*Math.sin(p2.latr) + Math.cos(p1.latr)*Math.cos(p2.latr)*Math.cos(p2.lonr-p1.lonr)) * RADIUS)
end
def reset_meta_data
@earliest_point = nil
@latest_point = nil
@highest_point = nil
@lowest_point = nil
@distance = 0.0
@bounds = Bounds.new
end
def update_meta_data(pt, last_pt)
@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)
unless pt.elevation.nil?
@lowest_point = pt if(@lowest_point.nil? or pt.elevation < @lowest_point.elevation)
@highest_point = pt if(@highest_point.nil? or pt.elevation > @highest_point.elevation)
end
@bounds.add(pt)
@distance += haversine_distance(last_pt, pt) unless last_pt.nil?
end
end
end end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,116 +21,129 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
# In GPX, a single Track can hold multiple Segments, each of which hold
# multiple points (in this library, those points are instances of
# TrackPoint). Each instance of this class has its own meta-data, including
# low point, high point, and distance. Of course, each track references an
# array of the segments that copmrise it, but additionally each track holds
# a reference to all of its points as one big array called "points".
class Track < Base
attr_reader :points, :bounds, :lowest_point, :highest_point, :distance
attr_accessor :segments, :name, :gpx_file
# Initialize a track from a XML::Node, or, if no :element option is # In GPX, a single Track can hold multiple Segments, each of which hold
# passed, initialize a blank Track object. # multiple points (in this library, those points are instances of
def initialize(opts = {}) # TrackPoint). Each instance of this class has its own meta-data, including
@gpx_file = opts[:gpx_file] # low point, high point, and distance. Of course, each track references an
@segments = [] # array of the segments that copmrise it, but additionally each track holds
@points = [] # a reference to all of its points as one big array called "points".
reset_meta_data class Track < Base
if(opts[:element]) attr_reader :points, :bounds, :lowest_point, :highest_point, :distance
trk_element = opts[:element] attr_accessor :segments, :name, :gpx_file
@name = (trk_element.at("name").inner_text rescue "")
trk_element.search("trkseg").each do |seg_element| # Initialize a track from a REXML::Element, or, if no :element option is
seg = Segment.new(:element => seg_element, :track => self, :gpx_file => @gpx_file) # passed, initialize a blank Track object.
update_meta_data(seg) def initialize(opts = {})
@segments << seg @gpx_file = opts[:gpx_file]
end @segments = []
@points = []
reset_meta_data
if(opts[:element])
trk_element = opts[:element]
@name = (trk_element.elements["child::name"].text rescue "")
XPath.each(trk_element, "child::trkseg") do |seg_element|
seg = Segment.new(:element => seg_element, :track => self)
update_meta_data(seg)
@segments << seg
@points.concat(seg.points) unless seg.nil?
end
end
end end
end
# Append a segment to this track, updating its meta data along the way.
# Append a segment to this track, updating its meta data along the way. def append_segment(seg)
def append_segment(seg) update_meta_data(seg)
update_meta_data(seg) @segments << seg
@segments << seg @points.concat(seg.points) unless seg.nil?
@points.concat(seg.points) unless seg.nil?
end
# Returns true if the given time occurs within any of the segments of this track.
def contains_time?(time)
segments.each do |seg|
return true if seg.contains_time?(time)
end end
return false
end
# Finds the closest point (to "time") within this track. Useful for # Returns true if the given time occurs within any of the segments of this track.
# correlating things like pictures, video, and other events, if you are def contains_time?(time)
# working with a timestamp. segments.each do |seg|
def closest_point(time) return true if seg.contains_time?(time)
segment = segments.select { |s| s.contains_time?(time) } end
segment.first return false
end
# Removes all points outside of a given area and updates the meta data.
# The "area" paremeter is usually a Bounds object.
def crop(area)
reset_meta_data
segments.each do |seg|
seg.crop(area)
update_meta_data(seg) unless seg.empty?
end end
segments.delete_if { |seg| seg.empty? }
end
# Deletes all points within a given area and updates the meta data. # Finds the closest point (to "time") within this track. Useful for
def delete_area(area) # correlating things like pictures, video, and other events, if you are
reset_meta_data # working with a timestamp.
segments.each do |seg| def closest_point(time)
seg.delete_area(area) segment = segments.find { |s| s.contains_time?(time) }
update_meta_data(seg) unless seg.empty? segment.closest_point(time)
end end
segments.delete_if { |seg| seg.empty? }
end
# Returns true if this track has no points in it. This should return # Removes all points outside of a given area and updates the meta data.
# true even when the track has empty segments. # The "area" paremeter is usually a Bounds object.
def empty? def crop(area)
(points.nil? or points.size.zero?) reset_meta_data
end segments.each do |seg|
seg.crop(area)
update_meta_data(seg) unless seg.empty?
end
segments.delete_if { |seg| seg.empty? }
end
# Prints out a friendly summary of this track (sans points). Useful for # Deletes all points within a given area and updates the meta data.
# debugging and sanity checks. def delete_area(area)
reset_meta_data
segments.each do |seg|
seg.delete_area(area)
update_meta_data(seg) unless seg.empty?
end
segments.delete_if { |seg| seg.empty? }
end
def to_s # Returns true if this track has no points in it. This should return
result = "Track \n" # true even when the track has empty segments.
result << "\tName: #{name}\n" def empty?
result << "\tSize: #{points.size} points\n" (points.nil? or points.size.zero?)
result << "\tSegments: #{segments.size} \n" end
result << "\tDistance: #{distance} km\n"
result << "\tLowest Point: #{lowest_point.elevation} \n"
result << "\tHighest Point: #{highest_point.elevation}\n "
result << "\tBounds: #{bounds.to_s}"
result
end
protected # Creates a new REXML::Element from the contents of this instance.
def to_xml
trk= Element.new('trk')
name_elem = Element.new('name')
name_elem.text = name
trk.elements << name_elem
segments.each do |seg|
trk.elements << seg.to_xml
end
trk
end
def update_meta_data(seg) # Prints out a friendly summary of this track (sans points). Useful for
@lowest_point = seg.lowest_point if(@lowest_point.nil? or seg.lowest_point.elevation < @lowest_point.elevation) # debugging and sanity checks.
@highest_point = seg.highest_point if(@highest_point.nil? or seg.highest_point.elevation > @highest_point.elevation) def to_s
@bounds.add(seg.bounds) result = "Track \n"
@distance += seg.distance result << "\tName: #{name}\n"
@points.concat(seg.points) result << "\tSize: #{points.size} points\n"
end result << "\tSegments: #{segments.size} \n"
result << "\tDistance: #{distance} km\n"
result << "\tLowest Point: #{lowest_point.elevation} \n"
result << "\tHighest Point: #{highest_point.elevation}\n "
result << "\tBounds: #{bounds.to_s}"
result
end
def reset_meta_data protected
@bounds = Bounds.new
@highest_point = nil
@lowest_point = nil
@distance = 0.0
@points = []
end
end def update_meta_data(seg)
@lowest_point = seg.lowest_point if(@lowest_point.nil? or seg.lowest_point.elevation < @lowest_point.elevation)
@highest_point = seg.highest_point if(@highest_point.nil? or seg.highest_point.elevation > @highest_point.elevation)
@bounds.add(seg.bounds)
@distance += seg.distance
@points.concat(seg.points)
end
def reset_meta_data
@bounds = Bounds.new
@highest_point = nil
@lowest_point = nil
@distance = 0.0
@points = []
end
end
end end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -21,15 +21,15 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
# Basically the same as a point, the TrackPoint class is supposed to # Basically the same as a point, the TrackPoint class is supposed to
# represent the points that are children of Segment elements. So, the only # represent the points that are children of Segment elements. So, the only
# real difference is that TrackPoints hold a reference to their parent # real difference is that TrackPoints hold a reference to their parent
# Segments. # Segments.
class TrackPoint < Point class TrackPoint < Point
attr_accessor :segment attr_accessor :segment
def initialize(opts = {}) def initialize(opts = {})
super(opts) super(opts)
@segment = opts[:segment] @segment = opts[:segment]
end end
end end
end end

View File

@ -1,3 +0,0 @@
module GPX
VERSION = "0.7"
end

View File

@ -1,5 +1,5 @@
#-- #--
# Copyright (c) 2006 Doug Fales # Copyright (c) 2006 Doug Fales
# #
# Permission is hereby granted, free of charge, to any person obtaining # Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the # a copy of this software and associated documentation files (the
@ -20,55 +20,54 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++ #++
module GPX module GPX
# This class supports the concept of a waypoint. Beware that this class has
# not seen much use yet, since WalkingBoss does not use waypoints right now.
class Waypoint < Point
SUB_ELEMENTS = %w{ magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid extensions ele} # This class supports the concept of a waypoint. Beware that this class has
# not seen much use yet, since WalkingBoss does not use waypoints right now.
class Waypoint < Point
attr_reader :gpx_file SUB_ELEMENTS = %q{ magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid extensions }
SUB_ELEMENTS.each { |sub_el| attr_accessor sub_el.to_sym }
# Not implemented attr_reader :gpx_file
def crop(area)
end
# Not implemented # Not implemented
def delete_area(area) def crop(area)
end
# Initializes a waypoint from a XML::Node.
def initialize(opts = {})
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)
else
opts.each do |key, value|
assignment_method = "#{key}="
if self.respond_to?(assignment_method)
self.send(assignment_method, value)
end
end
end end
end
# Prints out a friendly summary of this track (sans points). Useful for # Not implemented
# debugging and sanity checks. def delete_area(area)
def to_s
result = "Waypoint \n"
result << "\tName: #{name}\n"
result << "\tLatitude: #{lat} \n"
result << "\tLongitude: #{lon} \n"
result << "\tElevation: #{elevation}\n "
result << "\tTime: #{time}\n"
SUB_ELEMENTS.each do |sub_element_attribute|
val = self.send(sub_element_attribute)
result << "\t#{sub_element_attribute}: #{val}\n" unless val.nil?
end end
result
end # Initializes a waypoint from a REXML::Element.
end def initialize(opts = {})
wpt_elem = opts[:element]
super(:element => wpt_elem)
instantiate_with_text_elements(wpt_elem, SUB_ELEMENTS)
@gpx_file = opts[:gpx_file]
end
# Converts a waypoint to a REXML::Element.
def to_xml
wpt = Element.new('wpt')
wpt.attributes['lat'] = lat
wpt.attributes['lon'] = lon
if self.respond_to? :name
name_elem = Element.new('name')
name_elem.text = self.name
wpt.elements << name_elem
end
if self.respond_to? :sym
sym_elem = Element.new('sym')
sym_elem.text = self.sym
wpt.elements << sym_elem
end
if self.respond_to? :ele
elev_elem = Element.new('ele')
elev_elem.text = self.ele
wpt.elements << elev_elem
end
wpt
end
end
end end

View File

@ -1,12 +0,0 @@
require 'test/unit'
require 'gpx'
class GPX10Test < 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

View File

@ -1,48 +0,0 @@
require 'test/unit'
require 'gpx'
class GPXFileTest < Test::Unit::TestCase
ONE_TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/one_track.gpx")
WITH_OR_WITHOUT_ELEV_FILE = File.join(File.dirname(__FILE__), "gpx_files/with_or_without_elev.gpx")
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
def test_with_or_with_elev
gpx_file = GPX::GPXFile.new(:gpx_file => WITH_OR_WITHOUT_ELEV_FILE)
assert_equal(2, 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

File diff suppressed because one or more lines are too long

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.0"
creator="GPSBabel - http://www.gpsbabel.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/0"
xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">
<trk>
<name>ACTIVE LOG #74</name>
<number>82</number>
<trkseg>
<trkpt lat="37.378388923" lon="-122.063654875">
<ele>35.706055</ele>
<time>2007-11-23T23:49:43Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>

View File

@ -1,9 +1 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="Link2GPS - 2.0.2 - http://www.hiketech.com" xsi:schemaLocation="ttp://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"><metadata><name><![CDATA[routes.gpx]]></name><time>2006-01-02T08:55:34Z</time><bounds min_lat="39.989739" min_lon="-105.295285" max_lat="39.999840" max_lon="-105.214696"/></metadata><extensions/><rte><name><![CDATA[GRG-CA-TO]]></name><rtept lat="39.997298" lon="-105.292674"><name><![CDATA[GRG-CA]]></name><sym>Waypoint</sym><ele>1766.535</ele></rtept><rtept lat="39.995700" lon="-105.292805"><name><![CDATA[AMPTHT]]></name><sym>Waypoint</sym><ele>1854.735</ele></rtept><rtept lat="39.989739" lon="-105.295285"><name><![CDATA[TO]]></name><sym>Waypoint</sym><ele>2163.556</ele></rtept></rte><rte><name><![CDATA[SBDR-SBDR]]></name><rtept lat="39.999840" lon="-105.214696"><name><![CDATA[SBDR]]></name><sym>Waypoint</sym><ele>1612.965</ele></rtept></rte></gpx>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="Link2GPS - 2.0.2 - http://www.hiketech.com" xsi:schemaLocation="ttp://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"><metadata><name><![CDATA[routes.gpx]]></name><time>2006-01-02T08:55:34Z</time><bounds min_lat="39.989739" min_lon="-105.295285" max_lat="39.999840" max_lon="-105.214696"/></metadata><extensions/>
<rte><name><![CDATA[GRG-CA-TO]]></name>
<rtept lat="39.997298" lon="-105.292674"><name><![CDATA[GRG-CA]]></name><sym>Waypoint</sym><ele>1766.535</ele></rtept>
<rtept lat="39.995700" lon="-105.292805"><name><![CDATA[AMPTHT]]></name><sym>Waypoint</sym><ele>1854.735</ele></rtept>
<rtept lat="39.989739" lon="-105.295285"><name><![CDATA[TO]]></name><sym>Waypoint</sym><ele>2163.556</ele></rtept></rte>
<rte><name><![CDATA[SBDR-SBDR]]></name>
<rtept lat="39.999840" lon="-105.214696"><name><![CDATA[SBDR]]></name><sym>Waypoint</sym><ele>1612.965</ele></rtept></rte>
</gpx>

View File

@ -1,20 +1 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?><gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="Link2GPS - 2.0.2 - http://www.hiketech.com" xsi:schemaLocation="ttp://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"><metadata><name><![CDATA[waypoints.gpx]]></name><time>2006-01-02T08:55:21Z</time><bounds min_lat="25.061783" min_lon="-111.946110" max_lat="50.982883" max_lon="121.640267"/></metadata><extensions/><wpt lat="40.035557" lon="-105.248268"><name><![CDATA[001]]></name><sym>Waypoint</sym><ele>1639.161</ele></wpt><wpt lat="39.993070" lon="-105.296588"><name><![CDATA[002]]></name><sym>Waypoint</sym><ele>1955.192</ele></wpt><wpt lat="39.990151" lon="-105.295680"><name><![CDATA[003]]></name><sym>Waypoint</sym><ele>2129.91</ele></wpt><wpt lat="39.990157" lon="-105.295686"><name><![CDATA[004]]></name><sym>Waypoint</sym><ele>2136.399</ele></wpt><wpt lat="39.990134" lon="-105.295251"><name><![CDATA[005]]></name><sym>Waypoint</sym><ele>2174.612</ele></wpt><wpt lat="39.990116" lon="-105.295147"><name><![CDATA[006]]></name><sym>Waypoint</sym><ele>2156.106</ele></wpt><wpt lat="39.990099" lon="-105.295207"><name><![CDATA[007]]></name><sym>Waypoint</sym><ele>2155.145</ele></wpt><wpt lat="39.990067" lon="-105.295185"><name><![CDATA[008]]></name><sym>Waypoint</sym><ele>2152.021</ele></wpt><wpt lat="39.995700" lon="-105.292805"><name><![CDATA[AMPTHT]]></name><sym>Waypoint</sym><ele>1854.735</ele></wpt><wpt lat="38.855550" lon="-94.799017"><name><![CDATA[GARMIN]]></name><sym>Waypoint</sym><ele>325.0491</ele></wpt><wpt lat="39.997298" lon="-105.292674"><name><![CDATA[GRG-CA]]></name><sym>Waypoint</sym><ele>1766.535</ele></wpt><wpt lat="50.982883" lon="-1.463900"><name><![CDATA[GRMEUR]]></name><sym>Waypoint</sym><ele>35.93469</ele></wpt><wpt lat="33.330190" lon="-111.946110"><name><![CDATA[GRMPHX]]></name><sym>Waypoint</sym><ele>361.0981</ele></wpt><wpt lat="25.061783" lon="121.640267"><name><![CDATA[GRMTWN]]></name><sym>Waypoint</sym><ele>38.09766</ele></wpt><wpt lat="39.999840" lon="-105.214696"><name><![CDATA[SBDR]]></name><sym>Waypoint</sym><ele>1612.965</ele></wpt><wpt lat="39.989739" lon="-105.295285"><name><![CDATA[TO]]></name><sym>Waypoint</sym><ele>2163.556</ele></wpt><wpt lat="40.035301" lon="-105.254443"><name><![CDATA[VICS]]></name><sym>Waypoint</sym><ele>1535.34</ele></wpt></gpx>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="Link2GPS - 2.0.2 - http://www.hiketech.com" xsi:schemaLocation="ttp://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"><metadata><name><![CDATA[waypoints.gpx]]></name><time>2006-01-02T08:55:21Z</time><bounds min_lat="25.061783" min_lon="-111.946110" max_lat="50.982883" max_lon="121.640267"/></metadata><extensions/>
<wpt lat="40.035557" lon="-105.248268"><name><![CDATA[001]]></name><sym>Waypoint</sym><ele>1639.161</ele><cmt><![CDATA[001]]></cmt><desc><![CDATA[Just some waypoint...]]></desc></wpt>
<wpt lat="39.993070" lon="-105.296588"><name><![CDATA[002]]></name><sym>Waypoint</sym><ele>1955.192</ele></wpt>
<wpt lat="39.990151" lon="-105.295680"><name><![CDATA[003]]></name><sym>Waypoint</sym><ele>2129.91</ele></wpt>
<wpt lat="39.990157" lon="-105.295686"><name><![CDATA[004]]></name><sym>Waypoint</sym><ele>2136.399</ele></wpt>
<wpt lat="39.990134" lon="-105.295251"><name><![CDATA[005]]></name><sym>Waypoint</sym><ele>2174.612</ele></wpt>
<wpt lat="39.990116" lon="-105.295147"><name><![CDATA[006]]></name><sym>Waypoint</sym><ele>2156.106</ele></wpt>
<wpt lat="39.990099" lon="-105.295207"><name><![CDATA[007]]></name><sym>Waypoint</sym><ele>2155.145</ele></wpt>
<wpt lat="39.990067" lon="-105.295185"><name><![CDATA[008]]></name><sym>Waypoint</sym><ele>2152.021</ele></wpt>
<wpt lat="39.995700" lon="-105.292805"><name><![CDATA[AMPTHT]]></name><sym>Waypoint</sym><ele>1854.735</ele></wpt>
<wpt lat="38.855550" lon="-94.799017"><name><![CDATA[GARMIN]]></name><sym>Waypoint</sym><ele>325.0491</ele></wpt>
<wpt lat="39.997298" lon="-105.292674"><name><![CDATA[GRG-CA]]></name><sym>Waypoint</sym><ele>1766.535</ele></wpt>
<wpt lat="50.982883" lon="-1.463900"><name><![CDATA[GRMEUR]]></name><sym>Waypoint</sym><ele>35.93469</ele></wpt>
<wpt lat="33.330190" lon="-111.946110"><name><![CDATA[GRMPHX]]></name><sym>Waypoint</sym><ele>361.0981</ele></wpt>
<wpt lat="25.061783" lon="121.640267"><name><![CDATA[GRMTWN]]></name><sym>Waypoint</sym><ele>38.09766</ele></wpt>
<wpt lat="39.999840" lon="-105.214696"><name><![CDATA[SBDR]]></name><sym>Waypoint</sym><ele>1612.965</ele></wpt>
<wpt lat="39.989739" lon="-105.295285"><name><![CDATA[TO]]></name><sym>Waypoint</sym><ele>2163.556</ele></wpt>
<wpt lat="40.035301" lon="-105.254443"><name><![CDATA[VICS]]></name><sym>Waypoint</sym><ele>1535.34</ele></wpt>
</gpx>

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" creator="MapSource 6.5" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<metadata>
<link href="http://www.garmin.com">
<text>Garmin International</text>
</link>
<time>2007-04-16T18:11:47Z</time>
<bounds maxlat="40.207429" maxlon="116.670578" minlat="39.876895" minlon="101.636217"/>
</metadata>
<trk>
<name>ACTIVE LOG</name>
<trkseg>
<trkpt lat="40.079434" lon="116.295948">
<ele>50.606567</ele>
<time>2007-03-25T05:17:34Z</time>
</trkpt>
</trkseg>
</trk>
<trk>
<name>LINE-13</name>
<trkseg>
<trkpt lat="39.949744" lon="116.427398"/>
</trkseg>
</trk>
</gpx>

View File

@ -1,7 +1,7 @@
require 'test/unit' require 'test/unit'
require 'gpx' require File.dirname(__FILE__) + '/../lib/gpx'
class MagellanTest < Test::Unit::TestCase class TestMagellanTrackLog < Test::Unit::TestCase
MAGELLAN_TRACK_LOG = File.join(File.dirname(__FILE__), "gpx_files/magellan_track.log") MAGELLAN_TRACK_LOG = File.join(File.dirname(__FILE__), "gpx_files/magellan_track.log")
GPX_FILE = File.join(File.dirname(__FILE__), "gpx_files/one_segment.gpx") GPX_FILE = File.join(File.dirname(__FILE__), "gpx_files/one_segment.gpx")

View File

@ -1,115 +0,0 @@
require 'test/unit'
require 'fileutils'
require 'gpx'
class OutputTest < Test::Unit::TestCase
include GPX
def setup
FileUtils.mkdir_p(File.join(File.dirname(__FILE__), "output"))
end
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', :ele => '1766.535'},
{:lat => 33.330190, :lon => -111.946110, :name => 'GRMPHX', :sym => 'Waypoint', :ele => '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', :ele => '38.09766'},
{:lat => 39.999840, :lon => -105.214696, :name => 'SBDR', :sym => 'Waypoint', :ele => '1612.965'},
{:lat => 39.989739, :lon => -105.295285, :name => 'TO', :sym => 'Waypoint', :ele => '2163.556'},
{:lat => 40.035301, :lon => -105.254443, :name => 'VICS', :sym => 'Waypoint', :ele => '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
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 #{GPX::VERSION} Copyright 2006-2009 Doug Fales -- http://gpx.rubyforge.org/\" 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

@ -1,63 +0,0 @@
require 'test/unit'
require 'gpx'
class RouteTest < Test::Unit::TestCase
def test_read_routes
gpx = GPX::GPXFile.new(:gpx_file => File.join(File.dirname(__FILE__), "gpx_files/routes.gpx"))
assert_equal(2, gpx.routes.size)
first_route = gpx.routes.first
assert_equal(3, first_route.points.size)
assert_equal('GRG-CA-TO', first_route.name)
# Route 1, First Point
# <rtept lat="39.997298" lon="-105.292674">
# <name><![CDATA[GRG-CA]]></name>
# <sym>Waypoint</sym>
# <ele>1766.535</ele>
# </rtept>
assert_equal(39.997298, first_route.points[0].lat)
assert_equal(-105.292674, first_route.points[0].lon)
assert_equal(1766.535, first_route.points[0].elevation)
# Route 1, Second Point
# <rtept lat="39.995700" lon="-105.292805">
# <name><![CDATA[AMPTHT]]></name>
# <sym>Waypoint</sym>
# <ele>1854.735</ele>
# </rtept>
assert_equal(39.995700, first_route.points[1].lat)
assert_equal(-105.292805, first_route.points[1].lon)
assert_equal(1854.735, first_route.points[1].elevation)
# Route 1, Third Point
# <rtept lat="39.989739" lon="-105.295285">
# <name><![CDATA[TO]]></name>
# <sym>Waypoint</sym>
# <ele>2163.556</ele>
# </rtept>
assert_equal(39.989739, first_route.points[2].lat)
assert_equal(-105.295285, first_route.points[2].lon)
assert_equal(2163.556, first_route.points[2].elevation)
second_route = gpx.routes[1]
assert_equal(1, second_route.points.size)
assert_equal('SBDR-SBDR', second_route.name)
# Route 2, Only Point
# <rtept lat="39.999840" lon="-105.214696">
# <name><![CDATA[SBDR]]></name>
# <sym>Waypoint</sym>
# <ele>1612.965</ele>
# </rtept>
assert_equal(39.999840, second_route.points[0].lat)
assert_equal(-105.214696, second_route.points[0].lon)
assert_equal(1612.965, second_route.points[0].elevation)
end
end

View File

@ -1,8 +1,7 @@
require 'test/unit' require 'test/unit'
require 'yaml' require File.dirname(__FILE__) + '/../lib/gpx'
require 'gpx'
class SegmentTest < Test::Unit::TestCase class TestSegment < Test::Unit::TestCase
ONE_SEGMENT = File.join(File.dirname(__FILE__), "gpx_files/one_segment.gpx") ONE_SEGMENT = File.join(File.dirname(__FILE__), "gpx_files/one_segment.gpx")
def setup def setup
@ -12,11 +11,11 @@ class SegmentTest < Test::Unit::TestCase
def test_segment_read def test_segment_read
assert_equal(189, @segment.points.size) assert_equal(189, @segment.points.size)
assert_equal(1144433525, @segment.earliest_point.time.to_i) assert_equal("Fri Apr 07 18:12:05 UTC 2006", @segment.earliest_point.time.to_s)
assert_equal(1144437991, @segment.latest_point.time.to_i) assert_equal("Fri Apr 07 19:26:31 UTC 2006", @segment.latest_point.time.to_s)
assert_equal(1334.447, @segment.lowest_point.elevation) assert_equal(1334.447, @segment.lowest_point.elevation)
assert_equal(1480.087, @segment.highest_point.elevation) assert_equal(1480.087, @segment.highest_point.elevation)
assert_in_delta(6.98803359528853, @segment.distance, 0.001) assert_equal("6.98803359528853", @segment.distance.to_s)
end end
def test_segment_crop def test_segment_crop
@ -26,10 +25,10 @@ class SegmentTest < Test::Unit::TestCase
:max_lon=> -108.999000) :max_lon=> -108.999000)
@segment.crop(crop_rectangle) @segment.crop(crop_rectangle)
assert_equal(106, @segment.points.size) assert_equal(106, @segment.points.size)
assert_in_delta(4.11422061733046, @segment.distance, 0.001) assert_equal("4.11422061733046", @segment.distance.to_s)
assert_equal(1144435041, @segment.earliest_point.time.to_i) assert_equal("Fri Apr 07 18:37:21 UTC 2006", @segment.earliest_point.time.to_s)
assert_equal(1144437752, @segment.latest_point.time.to_i) assert_equal("Fri Apr 07 19:22:32 UTC 2006", @segment.latest_point.time.to_s)
assert_equal(1407.027, @segment.lowest_point.elevation) assert_equal(1407.027, @segment.lowest_point.elevation)
assert_equal(1480.087, @segment.highest_point.elevation) assert_equal(1480.087, @segment.highest_point.elevation)
assert_equal(39.173834, @segment.bounds.min_lat) assert_equal(39.173834, @segment.bounds.min_lat)
@ -44,10 +43,10 @@ class SegmentTest < Test::Unit::TestCase
:max_lat=> 39.188000, :max_lat=> 39.188000,
:max_lon=> -108.999000) :max_lon=> -108.999000)
@segment.delete_area(delete_rectangle) @segment.delete_area(delete_rectangle)
assert_equal(83, @segment.points.size) assert_equal(83, @segment.points.size)
assert_in_delta(3.35967118153605, @segment.distance, 0.001) assert_equal("3.35967118153605", @segment.distance.to_s)
assert_equal(1144433525, @segment.earliest_point.time.to_i) assert_equal("Fri Apr 07 18:12:05 UTC 2006", @segment.earliest_point.time.to_s)
assert_equal(1144437991, @segment.latest_point.time.to_i) assert_equal("Fri Apr 07 19:26:31 UTC 2006", @segment.latest_point.time.to_s)
assert_equal(1334.447, @segment.lowest_point.elevation) assert_equal(1334.447, @segment.lowest_point.elevation)
assert_equal(1428.176, @segment.highest_point.elevation) assert_equal(1428.176, @segment.highest_point.elevation)
assert_equal(39.180572, @segment.bounds.min_lat) assert_equal(39.180572, @segment.bounds.min_lat)

View File

@ -1,7 +1,7 @@
require 'test/unit' require 'test/unit'
require 'gpx' require File.dirname(__FILE__) + '/../lib/gpx'
class TrackFileTest < Test::Unit::TestCase class TestTrackFile < Test::Unit::TestCase
TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/tracks.gpx") TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/tracks.gpx")
OTHER_TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/arches.gpx") OTHER_TRACK_FILE = File.join(File.dirname(__FILE__), "gpx_files/arches.gpx")
@ -69,7 +69,7 @@ class TrackFileTest < Test::Unit::TestCase
end end
def test_write def test_write
@other_track_file.write("tests/output/myoutput.gpx") @other_track_file.write("myoutput.gpx")
end end
end end

View File

@ -1,7 +1,7 @@
require 'test/unit' require 'test/unit'
require 'gpx' require File.dirname(__FILE__) + '/../lib/gpx'
class TrackTest < Test::Unit::TestCase class TestTrack < Test::Unit::TestCase
ONE_TRACK = File.join(File.dirname(__FILE__), "gpx_files/one_track.gpx") ONE_TRACK = File.join(File.dirname(__FILE__), "gpx_files/one_track.gpx")
def setup def setup
@ -11,38 +11,38 @@ class TrackTest < Test::Unit::TestCase
def test_track_read def test_track_read
assert_equal("ACTIVE LOG", @track.name) assert_equal("ACTIVE LOG", @track.name)
assert_equal( 182, @track.points.size) assert_equal( 364, @track.points.size)
assert_equal(8, @track.segments.size) assert_equal(8, @track.segments.size)
assert_in_delta(3.07249668492626, @track.distance, 0.001) assert_equal("3.07249668492626", @track.distance.to_s)
assert_equal(1267.155, @track.lowest_point.elevation) assert_equal(1267.155, @track.lowest_point.elevation)
assert_equal(1594.003, @track.highest_point.elevation) assert_equal(1594.003, @track.highest_point.elevation)
assert_equal(38.681488, @track.bounds.min_lat) assert_equal(38.681488, @track.bounds.min_lat)
assert_equal(-109.606948, @track.bounds.min_lon) assert_equal(-109.606948, @track.bounds.min_lon)
assert_equal(38.791759, @track.bounds.max_lat) assert_equal(38.791759, @track.bounds.max_lat)
assert_equal(-109.447045, @track.bounds.max_lon) assert_equal(-109.447045, @track.bounds.max_lon)
end end
def test_track_crop def test_track_crop
area = GPX::Bounds.new( area = GPX::Bounds.new(
:min_lat => 38.710000, :min_lat => 38.710000,
:min_lon => -109.600000, :min_lon => -109.600000,
:max_lat => 38.791759, :max_lat => 38.791759,
:max_lon => -109.450000) :max_lon => -109.450000)
@track.crop(area) @track.crop(area)
assert_equal("ACTIVE LOG", @track.name) assert_equal("ACTIVE LOG", @track.name)
assert_equal( 111, @track.points.size) assert_equal( 111, @track.points.size)
assert_equal(4, @track.segments.size) assert_equal(4, @track.segments.size)
assert_in_delta(1.62136024923607, @track.distance, 0.001) assert_equal("1.62136024923607", @track.distance.to_s)
assert_equal(1557.954, @track.lowest_point.elevation) assert_equal(1557.954, @track.lowest_point.elevation)
assert_equal(1582.468, @track.highest_point.elevation) assert_equal(1582.468, @track.highest_point.elevation)
assert_equal(38.782511, @track.bounds.min_lat) assert_equal(38.782511, @track.bounds.min_lat)
assert_equal(-109.599781, @track.bounds.min_lon) assert_equal(-109.599781, @track.bounds.min_lon)
assert_equal(38.789527, @track.bounds.max_lat) assert_equal(38.789527, @track.bounds.max_lat)
assert_equal(-109.594996, @track.bounds.max_lon) assert_equal(-109.594996, @track.bounds.max_lon)
end end
def test_track_delete def test_track_delete
area = GPX::Bounds.new( area = GPX::Bounds.new(
:min_lat => 38.710000, :min_lat => 38.710000,
:min_lon => -109.600000, :min_lon => -109.600000,
:max_lat => 38.791759, :max_lat => 38.791759,
@ -51,14 +51,14 @@ class TrackTest < Test::Unit::TestCase
#puts @track #puts @track
#assert_equal("ACTIVE LOG", @track.name) #assert_equal("ACTIVE LOG", @track.name)
#assert_equal( 111, @track.points.size) #assert_equal( 111, @track.points.size)
#assert_equal(4, @track.segments.size) #assert_equal(4, @track.segments.size)
#assert_equal("1.62136024923607", @track.distance.to_s) #assert_equal("1.62136024923607", @track.distance.to_s)
#assert_equal(1557.954, @track.lowest_point.elevation) #assert_equal(1557.954, @track.lowest_point.elevation)
#assert_equal(1582.468, @track.highest_point.elevation) #assert_equal(1582.468, @track.highest_point.elevation)
#assert_equal(38.782511, @track.bounds.min_lat) #assert_equal(38.782511, @track.bounds.min_lat)
#assert_equal(-109.599781, @track.bounds.min_lon) #assert_equal(-109.599781, @track.bounds.min_lon)
#assert_equal(38.789527, @track.bounds.max_lat) #assert_equal(38.789527, @track.bounds.max_lat)
#assert_equal(-109.594996, @track.bounds.max_lon) #assert_equal(-109.594996, @track.bounds.max_lon)
end end

View File

@ -1,44 +0,0 @@
require 'test/unit'
require 'gpx'
class WaypointTest < Test::Unit::TestCase
def test_read_waypoints
gpx = GPX::GPXFile.new(:gpx_file => File.join(File.dirname(__FILE__), "gpx_files/waypoints.gpx"))
assert_equal(17, gpx.waypoints.size)
# First Waypoint
# <wpt lat="40.035557" lon="-105.248268">
# <name><![CDATA[001]]></name>
# <sym>Waypoint</sym>
# <ele>1639.161</ele>
# <cmt><![CDATA[001]]></cmt>
# <desc><![CDATA[Just some waypoint...]]></desc>
# </wpt>
first_wpt = gpx.waypoints[0]
assert_equal(40.035557, first_wpt.lat)
assert_equal(-105.248268, first_wpt.lon)
assert_equal('001', first_wpt.name)
assert_equal('001', first_wpt.cmt)
assert_equal('Just some waypoint...', first_wpt.desc)
assert_equal('Waypoint', first_wpt.sym)
assert_equal(1639.161, first_wpt.elevation)
# Second Waypoint
# <wpt lat="39.993070" lon="-105.296588">
# <name><![CDATA[002]]></name>
# <sym>Waypoint</sym>
# <ele>1955.192</ele>
# </wpt>
second_wpt = gpx.waypoints[1]
assert_equal(39.993070, second_wpt.lat)
assert_equal(-105.296588, second_wpt.lon)
assert_equal('002', second_wpt.name)
assert_equal('Waypoint', second_wpt.sym)
assert_equal(1955.192, second_wpt.elevation)
end
end