150 lines
5.5 KiB
Ruby
150 lines
5.5 KiB
Ruby
#--
|
|
# Copyright (c) 2006 Doug Fales
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
# a copy of this software and associated documentation files (the
|
|
# "Software"), to deal in the Software without restriction, including
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
# the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
#++
|
|
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
|
|
# passed, initialize a blank Track object.
|
|
def initialize(opts = {})
|
|
@gpx_file = opts[:gpx_file]
|
|
@segments = []
|
|
@points = []
|
|
reset_meta_data
|
|
if(opts[:element])
|
|
trk_element = opts[:element]
|
|
@name = (trk_element.find("child::gpx:name", @gpx_file.ns).first.content rescue "")
|
|
trk_element.find("child::gpx:trkseg", @gpx_file.ns).each do |seg_element|
|
|
seg = Segment.new(:element => seg_element, :track => self, :gpx_file => @gpx_file)
|
|
update_meta_data(seg)
|
|
@segments << seg
|
|
@points.concat(seg.points) unless seg.nil?
|
|
end
|
|
end
|
|
end
|
|
|
|
# Append a segment to this track, updating its meta data along the way.
|
|
def append_segment(seg)
|
|
update_meta_data(seg)
|
|
@segments << seg
|
|
@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
|
|
return false
|
|
end
|
|
|
|
# Finds the closest point (to "time") within this track. Useful for
|
|
# correlating things like pictures, video, and other events, if you are
|
|
# working with a timestamp.
|
|
def closest_point(time)
|
|
segment = segments.find { |s| s.contains_time?(time) }
|
|
segment.closest_point(time)
|
|
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
|
|
segments.delete_if { |seg| seg.empty? }
|
|
end
|
|
|
|
# Deletes all points within a given area and updates the meta data.
|
|
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
|
|
|
|
# Returns true if this track has no points in it. This should return
|
|
# true even when the track has empty segments.
|
|
def empty?
|
|
(points.nil? or points.size.zero?)
|
|
end
|
|
|
|
# Creates a new XML::Node from the contents of this instance.
|
|
def to_xml
|
|
trk= Node.new('trk')
|
|
name_elem = Node.new('name')
|
|
name_elem << name
|
|
trk << name_elem
|
|
segments.each do |seg|
|
|
trk << seg.to_xml
|
|
end
|
|
trk
|
|
end
|
|
|
|
# Prints out a friendly summary of this track (sans points). Useful for
|
|
# debugging and sanity checks.
|
|
def to_s
|
|
result = "Track \n"
|
|
result << "\tName: #{name}\n"
|
|
result << "\tSize: #{points.size} points\n"
|
|
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
|
|
|
|
protected
|
|
|
|
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
|