broutes 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|