broutes 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/lib/broutes/formats/factory.rb +22 -0
- data/lib/broutes/formats/fit_file.rb +41 -0
- data/lib/broutes/formats/gpx_route.rb +9 -0
- data/lib/broutes/formats/gpx_track.rb +30 -0
- data/lib/broutes/formats/tcx.rb +96 -0
- data/lib/broutes/formats.rb +9 -0
- data/lib/broutes/geo_point.rb +56 -0
- data/lib/broutes/geo_route.rb +109 -0
- data/lib/broutes/maths.rb +26 -0
- data/lib/broutes/version.rb +3 -0
- data/lib/broutes.rb +26 -0
- data/spec/broutes/formats/factory_spec.rb +34 -0
- data/spec/broutes/formats/fit_file_spec.rb +54 -0
- data/spec/broutes/formats/gpx_route_spec.rb +7 -0
- data/spec/broutes/formats/gpx_track_spec.rb +63 -0
- data/spec/broutes/formats/tcx_spec.rb +152 -0
- data/spec/broutes/geo_point_spec.rb +68 -0
- data/spec/broutes/geo_route_spec.rb +190 -0
- data/spec/broutes/maths_spec.rb +16 -0
- data/spec/broutes_spec.rb +26 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/support/garmin_training_center.tcx +115353 -0
- data/spec/support/gpx_route.xml +755 -0
- data/spec/support/no_gps_coordinates.tcx +264 -0
- data/spec/support/sample fit read log.txt +18 -0
- data/spec/support/sample.fit +0 -0
- data/spec/support/single_lap.tcx +10732 -0
- data/spec/support/single_lap_gpx_track.gpx +11087 -0
- data/spec/support/single_lap_gpx_track_no_elevation.gpx +2 -0
- data/spec/support/summary_no_points.tcx +52 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NzQyYjM1NDdiMDY2Njk3ZmJiOGY0NGM5YmMwYjU3MGVlNGZmMmE4Mg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YTJiZjQ3Nzk5NTA1YTE1NzYwODA2YTlkNjQzYmJiMDQxNTljYzM0Yg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZWRmMTYwMGY1OTQ1ODdhZWY2NjE1MzkyNmFjZDA5MzNlMjMxNmVkMzViMTA5
|
10
|
+
MGI4Yjk0ZTI2YTRkMjgwMDkxZjE5ZWZkMzE3MTQxN2M2YWU2OTAzYTNlMmQ4
|
11
|
+
YmYzM2JlOThiMTkxYzUzNmI2MjlmNDBmNmM5M2YzZWFkYmYzZjI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Y2U5NmE5OWIyMzVhYzA3Y2FlMmNhN2M0MjA5YjdlYTU2NjVjMWQ2MmVkZGUw
|
14
|
+
NzQ4NWY0MTFmMDQwYzNlMjNjZTZlNzVhNTU0ZmNlOTZkNTBlMjZlY2ZkMTM1
|
15
|
+
Y2E5NjEzZWFiNGFmYmM5OTAzNDUxZDc0NDVkNGI4OGFhYTc5MDE=
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Public : provides factory method for loading appropriate route file parser
|
2
|
+
module Broutes::Formats
|
3
|
+
class Factory
|
4
|
+
# Public : factory method
|
5
|
+
#
|
6
|
+
# format - Symbol describing the format [:gpx_track, :tcx, :fit]
|
7
|
+
#
|
8
|
+
# Returns a route file parser
|
9
|
+
def get(format)
|
10
|
+
case format
|
11
|
+
when :gpx_track, 'application/gpx+xml', /\.gpx$/
|
12
|
+
GpxTrack.new
|
13
|
+
when :tcx, 'application/vnd.garmin.tcx+xml', /\.tcx$/
|
14
|
+
Tcx.new
|
15
|
+
when :fit, 'application/vnd.ant.fit', /\.fit$/
|
16
|
+
FitFile.new
|
17
|
+
else
|
18
|
+
raise ArgumentError.new("Unrecognised format #{format}. Supported formats are :gpx_track, :tcx, :fit")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'fit'
|
2
|
+
|
3
|
+
module Broutes::Formats
|
4
|
+
class FitFile
|
5
|
+
|
6
|
+
def load(file, route)
|
7
|
+
fit_file = Fit::File.read(file)
|
8
|
+
Broutes.logger.info {"Started fit processing"}
|
9
|
+
i = 0
|
10
|
+
fit_file.records.select {|r| r.content && r.content.record_type == :record }.each do |r|
|
11
|
+
begin
|
12
|
+
pr = r.content
|
13
|
+
data = { time: record_time(r) }
|
14
|
+
data[:lat] = convert_position(pr.position_lat) if pr.respond_to?(:position_lat)
|
15
|
+
data[:lon] = convert_position(pr.position_long) if pr.respond_to?(:position_long)
|
16
|
+
data[:elevation] = pr.altitude if pr.respond_to?(:altitude)
|
17
|
+
[:distance, :heart_rate, :power, :speed, :cadence, :temperature].each do |m|
|
18
|
+
data[m] = pr.send(m) if pr.respond_to?(m)
|
19
|
+
end
|
20
|
+
|
21
|
+
route.add_point(data)
|
22
|
+
i += 1
|
23
|
+
rescue => e
|
24
|
+
Broutes.logger.debug {"#{e.message} for #{r}"}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
Broutes.logger.info {"Loaded #{i} data points"}
|
28
|
+
end
|
29
|
+
|
30
|
+
def convert_position(value)
|
31
|
+
(8.381903171539307e-08 * value).round(5)
|
32
|
+
end
|
33
|
+
|
34
|
+
def record_time(record)
|
35
|
+
utc_seconds = record.content.timestamp
|
36
|
+
utc_seconds += record.header.time_offset if record.header.compressed_timestamp?
|
37
|
+
Time.new(1989, 12, 31) + utc_seconds
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Broutes::Formats
|
4
|
+
class GpxTrack
|
5
|
+
|
6
|
+
def load(file, route)
|
7
|
+
doc = Nokogiri::XML(file)
|
8
|
+
Broutes.logger.info {"Loaded #{file} into #{doc.to_s.slice(0, 10)}"}
|
9
|
+
|
10
|
+
i = 0
|
11
|
+
doc.css('trkpt').each do |node|
|
12
|
+
p = route.add_point(lat: node['lat'].to_f, lon: node['lon'].to_f, elevation: point_elevation(node), time: point_time(node))
|
13
|
+
i += 1
|
14
|
+
end
|
15
|
+
Broutes.logger.info {"Loaded #{i} data points"}
|
16
|
+
end
|
17
|
+
|
18
|
+
def point_elevation(node)
|
19
|
+
if elevation_node = node.at_css('ele')
|
20
|
+
elevation_node.inner_text.to_f
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def point_time(node)
|
25
|
+
if time_node = node.at_css('time')
|
26
|
+
DateTime.parse(time_node.inner_text).to_time
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Broutes::Formats
|
4
|
+
class Tcx
|
5
|
+
def load(file, route)
|
6
|
+
doc = Nokogiri::XML(file)
|
7
|
+
Broutes.logger.info {"Loaded #{file} into #{doc.to_s.slice(0, 10)}"}
|
8
|
+
|
9
|
+
i = 0
|
10
|
+
doc.css('Trackpoint').each do |node|
|
11
|
+
data = {
|
12
|
+
elevation: point_elevation(node),
|
13
|
+
time: point_time(node),
|
14
|
+
distance: point_distance(node),
|
15
|
+
heart_rate: point_heart_rate(node),
|
16
|
+
power: point_power(node),
|
17
|
+
speed: point_speed(node),
|
18
|
+
cadence: point_cadence(node)
|
19
|
+
}
|
20
|
+
if location = point_location(node)
|
21
|
+
data[:lat] = location[0]
|
22
|
+
data[:lon] = location[1]
|
23
|
+
end
|
24
|
+
|
25
|
+
p = route.add_point(data)
|
26
|
+
i += 1
|
27
|
+
end
|
28
|
+
Broutes.logger.info {"Loaded #{i} data points"}
|
29
|
+
|
30
|
+
# Load in summary values if time and distance nil, ie no points
|
31
|
+
unless route.total_time
|
32
|
+
route.total_time = doc.css('Activities > Activity > Lap > TotalTimeSeconds').reduce(0) { |sum, node|
|
33
|
+
sum + node.inner_text.to_i
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
unless route.total_distance
|
38
|
+
route.total_distance = doc.css('Activities > Activity > Lap > DistanceMeters').reduce(0) { |sum, node|
|
39
|
+
sum + node.inner_text.to_i
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
unless route.started_at
|
44
|
+
route.started_at = DateTime.parse(doc.css('Activities > Activity > Lap').first['StartTime']).to_time
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def point_location(node)
|
49
|
+
if position_node = node.at_css('Position')
|
50
|
+
[ position_node.at_css('LatitudeDegrees').inner_text.to_f, position_node.at_css('LongitudeDegrees').inner_text.to_f ]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def point_distance(node)
|
55
|
+
if distance_node = node.at_css('DistanceMeters')
|
56
|
+
distance_node.inner_text.to_f
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def point_elevation(node)
|
61
|
+
if elevation_node = node.at_css('AltitudeMeters')
|
62
|
+
elevation_node.inner_text.to_f
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def point_time(node)
|
67
|
+
if time_node = node.at_css('Time')
|
68
|
+
DateTime.parse(time_node.inner_text).to_time
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def point_heart_rate(node)
|
73
|
+
if hr_node = node.at_css('HeartRateBpm')
|
74
|
+
hr_node.inner_text.to_i
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def point_cadence(node)
|
79
|
+
if cadence_node = node.at_css('Cadence')
|
80
|
+
cadence_node.inner_text.to_i
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def point_power(node)
|
85
|
+
if power_node = node.at_xpath('.//tpx:Watts', 'tpx' => 'http://www.garmin.com/xmlschemas/ActivityExtension/v2')
|
86
|
+
power_node.inner_text.to_i
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def point_speed(node)
|
91
|
+
if speed_node = node.at_xpath('.//tpx:Speed', 'tpx' => 'http://www.garmin.com/xmlschemas/ActivityExtension/v2')
|
92
|
+
speed_node.inner_text.to_f
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Broutes
|
4
|
+
class GeoPoint
|
5
|
+
attr_accessor :lat, :lon, :elevation, :distance, :heart_rate, :power, :speed, :cadence, :temperature
|
6
|
+
attr_reader :time
|
7
|
+
|
8
|
+
def initialize(args={})
|
9
|
+
args.each_pair do |key, value| send("#{key}=", value) if respond_to?("#{key}=") end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_hash(h)
|
13
|
+
GeoPoint.new(h)
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_location?
|
17
|
+
lat && lon
|
18
|
+
end
|
19
|
+
|
20
|
+
def time=(value)
|
21
|
+
if value.is_a?(String)
|
22
|
+
@time = DateTime.parse(value).to_time
|
23
|
+
else
|
24
|
+
@time = value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
lat == other.lat &&
|
30
|
+
lon == other.lon &&
|
31
|
+
elevation == other.elevation &&
|
32
|
+
distance == other.distance &&
|
33
|
+
time == other.time &&
|
34
|
+
heart_rate == other.heart_rate &&
|
35
|
+
power == other.power &&
|
36
|
+
speed == other.speed &&
|
37
|
+
cadence == other.cadence &&
|
38
|
+
temperature == other.temperature
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_hash
|
42
|
+
h = {}
|
43
|
+
h['lat'] = lat if lat
|
44
|
+
h['lon'] = lon if lon
|
45
|
+
h['elevation'] = elevation if elevation
|
46
|
+
h['distance'] = distance if distance
|
47
|
+
h['time'] = time if time
|
48
|
+
h['heart_rate'] = heart_rate if heart_rate
|
49
|
+
h['power'] = power if power
|
50
|
+
h['speed'] = speed if speed
|
51
|
+
h['cadence'] = cadence if cadence
|
52
|
+
h['temperature'] = temperature if temperature
|
53
|
+
h
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Broutes
|
2
|
+
class GeoRoute
|
3
|
+
|
4
|
+
attr_reader :start_point, :end_point, :started_at, :ended_at, :total_time
|
5
|
+
attr_writer :total_distance, :started_at
|
6
|
+
attr_accessor :total_time
|
7
|
+
|
8
|
+
def points
|
9
|
+
get_points.to_enum
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(args={})
|
13
|
+
args.each_pair do |key, value|
|
14
|
+
if key.to_sym == :points
|
15
|
+
value.each { |p| add_point(p) }
|
16
|
+
else
|
17
|
+
send("#{key}=", value) if respond_to?("#{key}=")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.from_hash(h)
|
23
|
+
route = GeoRoute.new h
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
h = {
|
28
|
+
'total_distance' => total_distance,
|
29
|
+
'total_time' => total_time,
|
30
|
+
'total_ascent' => total_ascent,
|
31
|
+
'total_descent' => total_ascent,
|
32
|
+
'points' => points.collect { |p| p.to_hash }
|
33
|
+
}
|
34
|
+
h['start_point'] = start_point.to_hash if start_point
|
35
|
+
h['end_point'] = end_point.to_hash if end_point
|
36
|
+
h['started_at'] = @started_at if @started_at
|
37
|
+
h
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_point(args)
|
41
|
+
point = GeoPoint.new(args)
|
42
|
+
if @start_point
|
43
|
+
if point.distance
|
44
|
+
@total_distance = point.distance
|
45
|
+
else
|
46
|
+
if distance = Maths.haversine_distance(@end_point, point)
|
47
|
+
@total_distance += distance
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@total_time = point.time - @start_point.time if point.time
|
52
|
+
else
|
53
|
+
@start_point = point
|
54
|
+
@total_distance = point.distance || 0
|
55
|
+
end
|
56
|
+
|
57
|
+
point.distance = @total_distance
|
58
|
+
process_elevation_delta(@end_point, point)
|
59
|
+
|
60
|
+
@end_point = point
|
61
|
+
get_points << point
|
62
|
+
end
|
63
|
+
|
64
|
+
def process_elevation_delta(last_point, new_point)
|
65
|
+
if last_point && new_point && last_point.elevation && new_point.elevation
|
66
|
+
delta = new_point.elevation - last_point.elevation
|
67
|
+
@_total_ascent = self.total_ascent + (delta > 0 ? delta : 0)
|
68
|
+
@_total_descent = self.total_descent - (delta < 0 ? delta : 0)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def started_at
|
73
|
+
return @started_at if @started_at
|
74
|
+
@start_point.time if @start_point
|
75
|
+
end
|
76
|
+
|
77
|
+
def ended_at
|
78
|
+
@end_point.time if @end_point
|
79
|
+
end
|
80
|
+
|
81
|
+
def total_ascent
|
82
|
+
@_total_ascent ||= 0
|
83
|
+
end
|
84
|
+
|
85
|
+
def total_descent
|
86
|
+
@_total_descent ||= 0
|
87
|
+
end
|
88
|
+
|
89
|
+
# Public : Total distance measured between points in whole metres
|
90
|
+
#
|
91
|
+
# Returns Float distance in m
|
92
|
+
def total_distance
|
93
|
+
@total_distance.round if @total_distance
|
94
|
+
end
|
95
|
+
|
96
|
+
# Public : Measure of how hilly the route is. Measured as total ascent (m) / distance (km)
|
97
|
+
#
|
98
|
+
# Returns Float measure
|
99
|
+
def hilliness
|
100
|
+
(total_distance > 0) ? (total_ascent * 1000 / total_distance) : 0
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def get_points
|
106
|
+
@_points ||= []
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Broutes
|
2
|
+
module Maths
|
3
|
+
class << self
|
4
|
+
def haversine_distance(p1, p2)
|
5
|
+
return unless p1.has_location? && p2.has_location?
|
6
|
+
|
7
|
+
dlat = p2.lat - p1.lat
|
8
|
+
dlon = p2.lon - p1.lon
|
9
|
+
|
10
|
+
dlon_rad = dlon * RAD_PER_DEG
|
11
|
+
dlat_rad = dlat * RAD_PER_DEG
|
12
|
+
|
13
|
+
lat1_rad = p1.lat * RAD_PER_DEG
|
14
|
+
lon1_rad = p1.lon * RAD_PER_DEG
|
15
|
+
|
16
|
+
lat2_rad = p2.lat * RAD_PER_DEG
|
17
|
+
lon2_rad = p2.lon * RAD_PER_DEG
|
18
|
+
|
19
|
+
a = (Math.sin(dlat_rad/2))**2 + Math.cos(lat1_rad) * Math.cos(lat2_rad) * (Math.sin(dlon_rad/2))**2
|
20
|
+
c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
|
21
|
+
|
22
|
+
EARTH_RADIUS * c
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/broutes.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Broutes
|
2
|
+
require 'logger'
|
3
|
+
require_relative 'broutes/geo_route'
|
4
|
+
require_relative 'broutes/geo_point'
|
5
|
+
require_relative 'broutes/maths'
|
6
|
+
require_relative 'broutes/formats'
|
7
|
+
|
8
|
+
RAD_PER_DEG = 0.017453293 # PI/180
|
9
|
+
EARTH_RADIUS = 6371000 #m
|
10
|
+
|
11
|
+
def self.from_file(file, format)
|
12
|
+
raise "unable to interpret format #{format}" unless processor = Formats::Factory.new.get(format)
|
13
|
+
Broutes.logger.debug {"found processor #{processor} for #{file}"}
|
14
|
+
route = GeoRoute.new
|
15
|
+
processor.load(file, route)
|
16
|
+
route
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_writer :logger
|
21
|
+
|
22
|
+
def logger
|
23
|
+
@logger ||= Logger.new(STDOUT)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Formats::Factory do
|
4
|
+
describe "#get" do
|
5
|
+
|
6
|
+
let(:factory) { Formats::Factory.new }
|
7
|
+
|
8
|
+
context "when :gpx_track passed" do
|
9
|
+
it "returns GpxTrack" do
|
10
|
+
factory.get(:gpx_track).should be_an_instance_of(Formats::GpxTrack)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
context "when 'application/vnd.ant.fit' passed" do
|
14
|
+
it "returns FitFile" do
|
15
|
+
factory.get('application/vnd.ant.fit').should be_an_instance_of(Formats::FitFile)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
context "when :fit passed" do
|
19
|
+
it "returns FitFile" do
|
20
|
+
factory.get(:fit).should be_an_instance_of(Formats::FitFile)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
context "when unrecognised" do
|
24
|
+
it "raises ArgumentError nil" do
|
25
|
+
expect { factory.get(random_string) }.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
context "tcx filename" do
|
29
|
+
it "returns Tcx" do
|
30
|
+
factory.get("2012-12-30-12-23.tcx").should be_an_instance_of(Formats::Tcx)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Formats::FitFile do
|
4
|
+
describe "#load" do
|
5
|
+
before(:all) do
|
6
|
+
@file = open_file('sample.fit')
|
7
|
+
@target = Formats::FitFile.new
|
8
|
+
@route = GeoRoute.new
|
9
|
+
|
10
|
+
@target.load(@file, @route)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "sets the start point lat" do
|
14
|
+
@route.start_point.lat.should eq(52.93066)
|
15
|
+
end
|
16
|
+
it "sets the start point lon" do
|
17
|
+
@route.start_point.lon.should eq(-1.22402)
|
18
|
+
end
|
19
|
+
it "sets the heart rate" do
|
20
|
+
@route.start_point.heart_rate.should eq(88)
|
21
|
+
end
|
22
|
+
it "sets the power" do
|
23
|
+
@route.start_point.power.should eq(96)
|
24
|
+
end
|
25
|
+
it "extracts the cadence" do
|
26
|
+
@route.start_point.cadence.should eq(51)
|
27
|
+
end
|
28
|
+
it "extracts the speed" do
|
29
|
+
@route.start_point.speed.should eq(4.024)
|
30
|
+
end
|
31
|
+
it "extracts the speed" do
|
32
|
+
@route.start_point.temperature.should eq(11.0)
|
33
|
+
end
|
34
|
+
it "sets the total distance" do
|
35
|
+
@route.total_distance.should eq(92068)
|
36
|
+
end
|
37
|
+
it "sets the total ascent" do
|
38
|
+
@route.total_ascent.round.should eq(1176)
|
39
|
+
end
|
40
|
+
it "sets the total descent" do
|
41
|
+
@route.total_descent.round.should eq(1177)
|
42
|
+
end
|
43
|
+
it "sets the total time" do
|
44
|
+
@route.total_time.round.should eq(13675)
|
45
|
+
end
|
46
|
+
it "sets the started_at" do
|
47
|
+
@route.started_at.to_i.should eq(Time.new(2012, 5, 12, 7, 29, 45).to_i)
|
48
|
+
end
|
49
|
+
it "sets the ended_at" do
|
50
|
+
@route.ended_at.to_i.should eq(Time.new(2012, 5, 12, 11, 17, 40).to_i)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Formats::GpxTrack do
|
4
|
+
describe "#load" do
|
5
|
+
before(:all) do
|
6
|
+
@file = open_file('single_lap_gpx_track.gpx')
|
7
|
+
@target = Formats::GpxTrack.new
|
8
|
+
@route = GeoRoute.new
|
9
|
+
|
10
|
+
@target.load(@file, @route)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "sets the start point lat" do
|
14
|
+
@route.start_point.lat.should eq(52.9552055)
|
15
|
+
end
|
16
|
+
it "sets the start point lon" do
|
17
|
+
@route.start_point.lon.should eq(-1.1558583)
|
18
|
+
end
|
19
|
+
it "sets the total distance" do
|
20
|
+
@route.total_distance.should eq(7088)
|
21
|
+
end
|
22
|
+
it "sets the total ascent" do
|
23
|
+
@route.total_ascent.round.should eq(34)
|
24
|
+
end
|
25
|
+
it "sets the total descent" do
|
26
|
+
@route.total_descent.round.should eq(37)
|
27
|
+
end
|
28
|
+
it "sets the total time" do
|
29
|
+
@route.total_time.round.should eq(1231)
|
30
|
+
end
|
31
|
+
it "sets the started_at" do
|
32
|
+
@route.started_at.to_i.should eq(Time.new(2011, 5, 19, 17, 57, 21).to_i)
|
33
|
+
end
|
34
|
+
it "sets the ended_at" do
|
35
|
+
@route.ended_at.to_i.should eq(Time.new(2011, 5, 19, 18, 17, 52).to_i)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
describe "when file doesn't have elevation" do
|
39
|
+
before(:all) do
|
40
|
+
@file = open_file('single_lap_gpx_track_no_elevation.gpx')
|
41
|
+
@target = Formats::GpxTrack.new
|
42
|
+
@route = GeoRoute.new
|
43
|
+
|
44
|
+
@target.load(@file, @route)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "sets the start point lat" do
|
48
|
+
@route.start_point.lat.should eq(52.926467718938)
|
49
|
+
end
|
50
|
+
it "sets the start point lon" do
|
51
|
+
@route.start_point.lon.should eq(-1.216092432889)
|
52
|
+
end
|
53
|
+
it "sets the total distance" do
|
54
|
+
@route.total_distance.should eq(123955)
|
55
|
+
end
|
56
|
+
it "sets the total ascent" do
|
57
|
+
@route.total_ascent.should eq(0)
|
58
|
+
end
|
59
|
+
it "sets the total descent" do
|
60
|
+
@route.total_descent.should eq(0)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|