Class: GPX::GPXFile

Inherits:
Base
  • Object
show all
Defined in:
lib/gpx/gpx_file.rb

Constant Summary collapse

DEFAULT_CREATOR =
"GPX RubyGem #{GPX::VERSION} -- http://dougfales.github.io/gpx/"

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#instantiate_with_text_elements

Constructor Details

#initialize(opts = {}) ⇒ GPXFile

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])


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/gpx/gpx_file.rb', line 27

def initialize(opts = {})
  super()
  @duration = 0
  @attributes = {}
  @namespace_defs = []
  @tracks = []
  @time = nil

  if opts[:gpx_file] || opts[:gpx_data]
    if opts[:gpx_file]
      gpx_file = opts[:gpx_file]
      gpx_file = File.open(gpx_file) unless gpx_file.respond_to?(:read)
      @xml = Nokogiri::XML(gpx_file)
    else
      @xml = Nokogiri::XML(opts[:gpx_data])
    end

    gpx_element = @xml.at('gpx')
    @attributes = gpx_element.attributes
    @namespace_defs = gpx_element.namespace_definitions
    @version = gpx_element['version']
    
    bounds_element = (
    begin
      @xml.at('metadata/bounds')
    rescue StandardError
      nil
    end)
    if bounds_element
      @bounds.min_lat = get_bounds_attr_value(bounds_element, %w[min_lat minlat minLat])
      @bounds.min_lon = get_bounds_attr_value(bounds_element, %w[min_lon minlon minLon])
      @bounds.max_lat = get_bounds_attr_value(bounds_element, %w[max_lat maxlat maxLat])
      @bounds.max_lon = get_bounds_attr_value(bounds_element, %w[max_lon maxlon maxLon])
    else
      get_bounds = true
    end

    @time = begin
      Time.parse(@xml.at('metadata/time').inner_text)
    rescue StandardError
      nil
    end
    @name = begin
      @xml.at('metadata/name').inner_text
    rescue StandardError
      nil
    end
    @description = begin
      @xml.at('metadata/desc').inner_text
    rescue StandardError
      nil
    end
    @xml.search('trk').each do |trk|
      trk = Track.new(element: trk, gpx_file: self)
      (trk, get_bounds)
      @tracks << trk
    end
    @waypoints = []
    @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(&:empty?)

    calculate_duration
  else
    
    opts.each { |attr_name, value| instance_variable_set("@#{attr_name}", value) }
    unless @tracks.nil? || @tracks.empty?
      @tracks.each { |trk| (trk) }
      calculate_duration
    end
  end
  @tracks ||= []
  @routes ||= []
  @waypoints ||= []
end

Instance Attribute Details

#boundsObject

Returns the value of attribute bounds.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def bounds
  @bounds
end

#creatorObject

Returns the value of attribute creator.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def creator
  @creator
end

#descriptionObject

Returns the value of attribute description.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def description
  @description
end

#durationObject

Returns the value of attribute duration.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def duration
  @duration
end

#highest_pointObject

Returns the value of attribute highest_point.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def highest_point
  @highest_point
end

#lowest_pointObject

Returns the value of attribute lowest_point.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def lowest_point
  @lowest_point
end

#moving_durationObject

Returns the value of attribute moving_duration.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def moving_duration
  @moving_duration
end

#nameObject

Returns the value of attribute name.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def name
  @name
end

#nsObject

Returns the value of attribute ns.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def ns
  @ns
end

#routesObject

Returns the value of attribute routes.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def routes
  @routes
end

#timeObject

Returns the value of attribute time.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def time
  @time
end

#tracksObject

Returns the value of attribute tracks.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def tracks
  @tracks
end

#versionObject

Returns the value of attribute version.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def version
  @version
end

#waypointsObject

Returns the value of attribute waypoints.



5
6
7
# File 'lib/gpx/gpx_file.rb', line 5

def waypoints
  @waypoints
end

Instance Method Details

#average_speed(opts = { units: 'kilometers' }) ⇒ Object

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 sum of duration of all segments of all tracks (not taking into accounting pause time).



135
136
137
138
139
140
141
142
143
144
# File 'lib/gpx/gpx_file.rb', line 135

def average_speed(opts = { units: 'kilometers' })
  case opts[:units]
  when /kilometers/i
    distance / (moving_duration / 3600.0)
  when /meters/i
    (distance * 1000) / (moving_duration / 3600.0)
  when /miles/i
    (distance * 0.62) / (moving_duration / 3600.0)
  end
end

#crop(area) ⇒ Object

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.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/gpx/gpx_file.rb', line 150

def crop(area)
  
  keep_tracks = []
  tracks.each do |trk|
    trk.crop(area)
    unless trk.empty?
      (trk)
      keep_tracks << trk
    end
  end
  @tracks = keep_tracks
  routes.each { |rte| rte.crop(area) }
  waypoints.each { |wpt| wpt.crop(area) }
end

#delete_area(area) ⇒ Object

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.



172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/gpx/gpx_file.rb', line 172

def delete_area(area)
  
  keep_tracks = []
  tracks.each do |trk|
    trk.delete_area(area)
    unless trk.empty?
      (trk)
      keep_tracks << trk
    end
  end
  @tracks = keep_tracks
  routes.each { |rte| rte.delete_area(area) }
  waypoints.each { |wpt| wpt.delete_area(area) }
end

#distance(opts = { units: 'kilometers' }) ⇒ Object

Returns the distance, in kilometers, meters, or miles, of all of the tracks and segments contained in this GPXFile.



120
121
122
123
124
125
126
127
128
129
# File 'lib/gpx/gpx_file.rb', line 120

def distance(opts = { units: 'kilometers' })
  case opts[:units]
  when /kilometers/i
    @distance
  when /meters/i
    (@distance * 1000)
  when /miles/i
    (@distance * 0.62)
  end
end

#get_bounds_attr_value(el, possible_names) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/gpx/gpx_file.rb', line 104

def get_bounds_attr_value(el, possible_names)
  result = nil
  possible_names.each do |name|
    result = el[name]
    break unless result.nil?
  end
  (
  begin
    result.to_f
  rescue StandardError
    nil
  end)
end

#inspectObject



224
225
226
# File 'lib/gpx/gpx_file.rb', line 224

def inspect
  "<#{self.class.name}:...>"
end

#recalculate_distanceObject



228
229
230
231
232
233
234
# File 'lib/gpx/gpx_file.rb', line 228

def recalculate_distance
  @distance = 0
  @tracks.each do |track|
    track.recalculate_distance
    @distance += track.distance
  end
end

#reset_meta_dataObject

Resets the meta data for this GPX file. Meta data includes the bounds, the high and low points, and the distance.



189
190
191
192
193
194
195
# File 'lib/gpx/gpx_file.rb', line 189

def 
  @bounds = Bounds.new
  @highest_point = nil
  @lowest_point = nil
  @distance = 0.0
  @moving_duration = 0.0
end

#to_s(update_time = true) ⇒ Object



218
219
220
221
222
# File 'lib/gpx/gpx_file.rb', line 218

def to_s(update_time = true)
  @time = Time.now if @time.nil? || update_time
  doc = generate_xml_doc
  doc.to_xml
end

#update_meta_data(trk, get_bounds = true) ⇒ Object

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.



201
202
203
204
205
206
207
# File 'lib/gpx/gpx_file.rb', line 201

def (trk, get_bounds = true)
  @lowest_point = trk.lowest_point if @lowest_point.nil? || (!trk.lowest_point.nil? && (trk.lowest_point.elevation < @lowest_point.elevation))
  @highest_point = trk.highest_point if @highest_point.nil? || (!trk.highest_point.nil? && (trk.highest_point.elevation > @highest_point.elevation))
  @bounds.add(trk.bounds) if get_bounds
  @distance += trk.distance
  @moving_duration += trk.moving_duration
end

#write(filename, update_time = true) ⇒ Object

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.



211
212
213
214
215
216
# File 'lib/gpx/gpx_file.rb', line 211

def write(filename, update_time = true)
  @time = Time.now if @time.nil? || update_time
  @name ||= File.basename(filename)
  doc = generate_xml_doc
  File.open(filename, 'w+') { |f| f.write(doc.to_xml) }
end