piste_pal 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 893460445f1be425c25ce56ae12ef4e629c77c91dbed7e90667a0659a1d74bf6
4
+ data.tar.gz: da58a5516d4f98b355a6bc2fe2f3b55fe5b5b31300818bf7915982688cfce609
5
+ SHA512:
6
+ metadata.gz: 006fe9c2eb0e264d762fadd5cccc2aabbd9a58b901e16cacf153e85e3727d045c9d3cd5914a51559f6ba811a0272212f91b980e892583f3451547ebd01461f81
7
+ data.tar.gz: c2a92c099977e96d9c18df1fc6d7b094a076f4dd149486d1232f99d194bd2a7d451da4f8081d5a993dd41e5940d645ca76d8e8a43d1c7d733e505afc1afbd51b
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.3
7
+ before_install: gem install bundler -v 2.0.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in piste_pal.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,44 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ piste_pal (1.0.0)
5
+ nokogiri (~> 1.10)
6
+ rake (~> 10.0)
7
+ vincenty_distance (~> 1.0.0)
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ byebug (11.0.1)
13
+ diff-lcs (1.3)
14
+ mini_portile2 (2.4.0)
15
+ nokogiri (1.10.3)
16
+ mini_portile2 (~> 2.4.0)
17
+ rake (10.5.0)
18
+ rspec (3.8.0)
19
+ rspec-core (~> 3.8.0)
20
+ rspec-expectations (~> 3.8.0)
21
+ rspec-mocks (~> 3.8.0)
22
+ rspec-core (3.8.2)
23
+ rspec-support (~> 3.8.0)
24
+ rspec-expectations (3.8.4)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.8.0)
27
+ rspec-mocks (3.8.1)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.8.0)
30
+ rspec-support (3.8.2)
31
+ vincenty_distance (1.0.0)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ bundler (~> 2.0)
38
+ byebug (~> 11.0)
39
+ piste_pal!
40
+ rake (~> 10.0)
41
+ rspec (~> 3.0)
42
+
43
+ BUNDLED WITH
44
+ 2.0.1
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Josh Menden
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # PistePal
2
+
3
+ Fill your time off the slopes thinking about them. This gem takes .gpx files from popular activity tracking apps and turns them into fun stats.
4
+
5
+ The code can be accessed at a high level by "purchasing" either Day Passes or Season Passes, but most of the functionality is modularized to allow you access to the stats you want.
6
+
7
+ It is tailored for .gpx files exported from the [Slopes](https://getslopes.com/) app for now.
8
+
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'piste_pal'
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install piste_pal
25
+
26
+ ## Usage
27
+
28
+ ```ruby
29
+ require 'piste_pal'
30
+
31
+ # "Purchase" your day pass with a single .gpx data object
32
+
33
+ gpx_data = File.read('/path/to/gpx/file')
34
+ sundance = PistePal::DayPass.purchase(gpx_data)
35
+
36
+ sundance.resort # Sundance Mountain Resort
37
+ sundance.date # 2019-03-01 00:00:00 -0700
38
+ sundance.maximum_speed # {:value=>30.290415, :unit=>"mph"}
39
+ sundance.peak_altitude # {:value=>8261.18916855752, :unit=>"feet"}
40
+ sundance.vertical # {:value=>3797.6879430483204, :unit=>"feet"}
41
+ sundance.distance # {:value=>4.796952318157319, :unit=>"miles"}
42
+ sundance.tallest_run # {:value=>1282.958728718721, :unit=>"feet"}
43
+ sundance.longest_run # {:value=>1.9052295991405417, :unit=>"miles"}
44
+ sundance.runs # [[@trackpoint1, @trackpoint2, ...], [@trackpoint1, @trackpoint2, ...]]
45
+ sundance.lifts # [[@trackpoint1, @trackpoint2, ...], [@trackpoint1, @trackpoint2, ...]]
46
+
47
+ # Or, purchase a season pass with an array of .gpx data objects
48
+
49
+ season_data = []
50
+ season_data.push(File.read('/path/to/gpx/file1'))
51
+ season_data.push(File.read('/path/to/gpx/file2'))
52
+ season_data.push(File.read('/path/to/gpx/file3'))
53
+
54
+ season = PistePal::SeasonPass.purchase(season_data)
55
+
56
+ season.days # [<PistePal::DayPass>, <PistePal::DayPass>, ...]
57
+ season.days(timestamp_only: true) # [2019-03-30 00:00:00 -0600, 2019-03-01 00:00:00 -0700, ...]
58
+ season.runs # 134
59
+ season.resorts # ["Sundance Resort", "Solitude Mountain Resort", "Park City Mountain Resort"]
60
+ season.vertical # {:value=>51900.38487275453, :unit=>"feet"}
61
+ season.distance # {:value=>52.35386405859661, :unit=>"miles"}
62
+ ```
63
+
64
+ ## Contributing
65
+
66
+ Bug reports and pull requests are welcome on [GitHub](https://github.com/joshmenden/piste_pal). No PR is too small.
67
+
68
+ Some things that need cleaning up / fixing:
69
+
70
+ * Should probably write some tests...
71
+ * Cleaner, more comprehensive way to handle different measurement systems (Metric vs. Imperial). I have this half-baked in some places but it could use a lot of improvment.
72
+ * Save trackpoint nodes to object and only extract data when necessary -- that is, only calculate the maximum_velocity when that method is invoked
73
+ * Check compatibility with files exported from other Apps (right now it is mostly tailored towards the "Slopes" app)
74
+
75
+ ## License
76
+
77
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "piste_pal"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,25 @@
1
+ require 'nokogiri'
2
+ require 'time'
3
+ module PistePal
4
+ module DataServices
5
+ class DateAndResort
6
+ def self.call
7
+ new().call
8
+ end
9
+
10
+ def call
11
+ name_node = @doc.xpath("//xmlns:name")
12
+ @date, @resort_name = name_node.children.first.text.strip.split(" - ")
13
+ @date = Time.parse(@date)
14
+ return [@date, @resort_name]
15
+ end
16
+
17
+ private
18
+
19
+ def initialize
20
+ @doc = PistePal::DataServices::GpxDoc.instance.doc
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,51 @@
1
+ require 'vincenty'
2
+ module PistePal
3
+ module DataServices
4
+ class Distance
5
+ def self.call(trackpoints:)
6
+ new(trackpoints: trackpoints).call
7
+ end
8
+
9
+ def call
10
+ if @trackpoints.first.is_a? Array
11
+ distance = 0
12
+ @trackpoints.each do |trackpoints|
13
+ distance += calculate_distance trackpoints
14
+ end
15
+ else
16
+ distance = calculate_distance @trackpoints
17
+ end
18
+ distance
19
+ end
20
+
21
+ private
22
+
23
+ def initialize(trackpoints:)
24
+ @trackpoints = trackpoints
25
+ end
26
+
27
+ def calculate_distance trackpoints
28
+ distance = 0
29
+ total_points = trackpoints.count - 1
30
+
31
+ point_a = nil
32
+ point_b = nil
33
+
34
+ for i in 0..total_points do
35
+ if i == 0
36
+ point_a = { latitude: trackpoints[i].lat, longitude: trackpoints[i].lon }
37
+ next
38
+ end
39
+
40
+ point_b = { latitude: trackpoints[i].lat, longitude: trackpoints[i].lon }
41
+ distance += Vincenty.distance_between_points(point_a, point_b)
42
+ point_a = point_b
43
+ end
44
+
45
+ distance
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+
@@ -0,0 +1,24 @@
1
+ require 'nokogiri'
2
+ module PistePal
3
+ module DataServices
4
+ class GpxDoc
5
+ # include Singleton
6
+ attr_accessor :doc
7
+
8
+ def self.set_instance(file_content)
9
+ @instance = new(file_content)
10
+ end
11
+ def self.instance
12
+ @instance || nil
13
+ end
14
+
15
+ private
16
+ def initialize file_content
17
+ # TODO: Support Nokogiri reading from http website instead of file path
18
+ @doc = Nokogiri::XML(file_content) do |config|
19
+ config.strict.noblanks
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ module PistePal
2
+ module DataServices
3
+ class MaxSpeedAndAltitude
4
+ def self.call(trackpoints:)
5
+ new(trackpoints).call
6
+ end
7
+
8
+ def call
9
+ max_speed_and_altitude
10
+ end
11
+
12
+ private
13
+
14
+ def initialize(trackpoints)
15
+ @trackpoints = trackpoints
16
+ end
17
+
18
+ def max_speed_and_altitude
19
+ max_speed = 0
20
+ max_alt = 0
21
+ @trackpoints.each do |trackpoint|
22
+ max_speed = trackpoint.speed if trackpoint.speed > max_speed
23
+ max_alt = trackpoint.elevation if trackpoint.elevation > max_alt
24
+ end
25
+ [max_speed, max_alt]
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,119 @@
1
+ module PistePal
2
+ module DataServices
3
+ class RunsAndLifts
4
+
5
+ MINIMUM_DEVIATION = 5
6
+
7
+ def self.call(trackpoints:)
8
+ new(trackpoints: trackpoints).call
9
+ end
10
+
11
+ def call
12
+ runs_and_lifts
13
+ end
14
+
15
+ private
16
+
17
+ def initialize(trackpoints:)
18
+ @trackpoints = trackpoints
19
+ end
20
+
21
+ def runs_and_lifts
22
+ group_runs_and_lifts(aggregate_runs_by_direction)
23
+ end
24
+
25
+ def group_runs_and_lifts groupings
26
+ runs = []
27
+ lifts = []
28
+ total_groups_count = groupings.count - 1
29
+
30
+ previous_group = nil
31
+
32
+ for i in 1..total_groups_count do
33
+ previous_group = groupings[i - 1] if i == 1
34
+
35
+ if groupings[i][:trackpoints].count > MINIMUM_DEVIATION
36
+ if groupings[i][:direction] == previous_group[:direction]
37
+ previous_group[:trackpoints].concat(groupings[i][:trackpoints])
38
+ else
39
+ if previous_group[:direction] == "ascending"
40
+ lifts.push(previous_group)
41
+ else
42
+ runs.push(previous_group)
43
+ end
44
+ previous_group = groupings[i]
45
+ end
46
+ elsif groupings[i][:trackpoints].count <= MINIMUM_DEVIATION
47
+ previous_group[:trackpoints].concat(groupings[i][:trackpoints])
48
+ end
49
+
50
+ if i == total_groups_count
51
+ if previous_group[:direction] == "ascending"
52
+ lifts.push(previous_group)
53
+ else
54
+ runs.push(previous_group)
55
+ end
56
+ end
57
+ end
58
+
59
+ # Not a perfect system, but for now let's delete the runs that have bad data
60
+ runs = runs.delete_if {|run| run[:trackpoints].first.elevation < run[:trackpoints].last.elevation}
61
+
62
+ [runs.map {|run| run[:trackpoints]}, lifts.map {|lift| lift[:trackpoints]}]
63
+ end
64
+
65
+ def aggregate_runs_by_direction
66
+ groupings = []
67
+ current_group = []
68
+ total_points = @trackpoints.count - 1
69
+ descending = true
70
+
71
+ for i in 1..total_points do
72
+ if i == 1
73
+ current_group.push(@trackpoints[i - 1])
74
+ current_group.push(@trackpoints[i])
75
+ if @trackpoints[i].elevation < @trackpoints[i - 1].elevation
76
+ descending = true
77
+ elsif @trackpoints[i].elevation > @trackpoints[i - 1].elevation
78
+ descending = false
79
+ # else they are equal, and it doesn't matter what direction we choose to start
80
+ end
81
+ next
82
+ end
83
+
84
+ if @trackpoints[i].elevation < @trackpoints[i - 1].elevation
85
+ if !descending
86
+ groupings.push({ direction: descending ? "descending" : "ascending", trackpoints: current_group })
87
+ current_group = []
88
+ descending = true
89
+ end
90
+ elsif @trackpoints[i].elevation > @trackpoints[i - 1].elevation
91
+ if descending
92
+ groupings.push({ direction: descending ? "descending" : "ascending", trackpoints: current_group })
93
+ current_group = []
94
+ descending = false
95
+ end
96
+ end
97
+
98
+ current_group.push(@trackpoints[i])
99
+
100
+ if i == total_points
101
+ groupings.push({ direction: descending ? "descending" : "ascending", trackpoints: current_group })
102
+ end
103
+
104
+ end
105
+ #
106
+ # groupings.each do |group|
107
+ # puts "\n\nNew Group:\nDirection: #{group[:direction]}\n"
108
+ # group[:trackpoints].each do |point|
109
+ # puts point.elevation
110
+ # end
111
+ # end
112
+ groupings
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+
119
+
@@ -0,0 +1,30 @@
1
+ module PistePal
2
+ module DataServices
3
+ class TallestAndLongestRun
4
+ def self.call(runs:)
5
+ new(runs: runs).call
6
+ end
7
+
8
+ def call
9
+ tallest = 0
10
+ longest = 0
11
+ @runs.each do |run|
12
+ vertical = run.first.elevation - run.last.elevation
13
+ distance = PistePal::DataServices::Distance.call(trackpoints: run)
14
+
15
+ tallest = vertical if vertical > tallest
16
+ longest = distance if distance > longest
17
+ end
18
+
19
+ [tallest, longest]
20
+ end
21
+
22
+ private
23
+
24
+ def initialize(runs:)
25
+ @runs = runs
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,50 @@
1
+ require 'nokogiri'
2
+ module PistePal
3
+ module DataServices
4
+ class Trackpoints
5
+
6
+ MINIMUM_ACCEPTABLE_DOP = 10
7
+
8
+ def self.call
9
+ new().call
10
+ end
11
+
12
+ def call
13
+ extract_trackpoints
14
+ @trackpoints
15
+ end
16
+
17
+ private
18
+
19
+ def initialize()
20
+ @doc = PistePal::DataServices::GpxDoc.instance.doc
21
+ @trackpoints = []
22
+ end
23
+
24
+ def extract_trackpoints
25
+ trackpoints = @doc.xpath("//xmlns:trkpt")
26
+ trackpoints.each do |tp|
27
+ trackpoint = PistePal::Trackpoint.new(**extract_params_from_trackpoint_node(tp))
28
+ @trackpoints.push(trackpoint) if trackpoint.hdop <= MINIMUM_ACCEPTABLE_DOP && trackpoint.vdop <= MINIMUM_ACCEPTABLE_DOP
29
+ end
30
+ end
31
+
32
+ def extract_params_from_trackpoint_node trackpoint
33
+ params = Hash.new
34
+ params[:lat] = trackpoint.xpath('@lat').to_s.to_f
35
+ params[:lon] = trackpoint.xpath('@lon').to_s.to_f
36
+ trackpoint.children.each do |child|
37
+ params[:elevation] = child.text.strip.to_f if child.name == "ele"
38
+ params[:time] = child.text.strip if child.name == "time"
39
+ params[:hdop] = child.text.strip.to_i if child.name == "hdop"
40
+ params[:vdop] = child.text.strip.to_i if child.name == "vdop"
41
+ end
42
+ extensions = trackpoint.children.search("extensions").first
43
+ extensions.children.each do |child|
44
+ params[:speed] = child.attributes["speed"].value.to_s.to_f if child.name == "gps" && !child.attributes["speed"].nil?
45
+ end
46
+ return params
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ module PistePal
2
+ module DataServices
3
+ class Vertical
4
+ def self.call(trackpoints:)
5
+ new(trackpoints: trackpoints).call
6
+ end
7
+
8
+ def call
9
+ vertical = 0
10
+ @trackpoints.each do |trackpoints|
11
+ vertical += (trackpoints.first.elevation - trackpoints.last.elevation)
12
+ end
13
+ vertical
14
+ end
15
+
16
+ private
17
+
18
+ def initialize(trackpoints:)
19
+ @trackpoints = trackpoints
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+
@@ -0,0 +1,100 @@
1
+ module PistePal
2
+ class DayPass
3
+ attr_accessor :resort, :date, :trackpoints, :maximum_speed, :peak_altitude, :vertical, :distance, :tallest_run, :longest_run, :runs, :lifts
4
+
5
+ def self.purchase file_content
6
+ new(file_content: file_content)
7
+ end
8
+
9
+ # convert mp/h to km/h
10
+ def maximum_speed(system = "imperial")
11
+ if system == "metric"
12
+ value = (@maximum_speed * 1.60934)
13
+ unit = "kmh"
14
+ elsif system == "imperial"
15
+ value = @maximum_speed
16
+ unit = "mph"
17
+ end
18
+
19
+ { :value => value, :unit => unit }
20
+ end
21
+
22
+ # convert meters to feet
23
+ def peak_altitude(system = "imperial")
24
+ if system == "metric"
25
+ value = @peak_altitude
26
+ unit = "meters"
27
+ elsif system == "imperial"
28
+ value = @peak_altitude * 3.28084
29
+ unit = "feet"
30
+ end
31
+
32
+ { :value => value, :unit => unit }
33
+ end
34
+
35
+ # convert meters to km / miles
36
+ def distance(system = "imperial")
37
+ if system == "metric"
38
+ value = @distance / 1000
39
+ unit = "kilometers"
40
+ elsif system == "imperial"
41
+ value = @distance / 1609.344
42
+ unit = "miles"
43
+ end
44
+
45
+ { :value => value, :unit => unit }
46
+ end
47
+
48
+ # convert meters to feet
49
+ def vertical(system = "imperial")
50
+ if system == "metric"
51
+ value = @vertical
52
+ unit = "meters"
53
+ elsif system == "imperial"
54
+ value = @vertical * 3.28084
55
+ unit = "feet"
56
+ end
57
+
58
+ { :value => value, :unit => unit }
59
+ end
60
+
61
+ # convert meters to feet
62
+ def tallest_run(system = "imperial")
63
+ if system == "metric"
64
+ value = @tallest_run
65
+ unit = "meters"
66
+ elsif system == "imperial"
67
+ value = @tallest_run * 3.28084
68
+ unit = "feet"
69
+ end
70
+
71
+ { :value => value, :unit => unit }
72
+ end
73
+
74
+ # convert meters to miles
75
+ def longest_run(system = "imperial")
76
+ if system == "metric"
77
+ value = @longest_run / 1000
78
+ unit = "kilometers"
79
+ elsif system == "imperial"
80
+ value = @longest_run / 1609.344
81
+ unit = "miles"
82
+ end
83
+
84
+ { :value => value, :unit => unit }
85
+ end
86
+
87
+ private
88
+
89
+ def initialize(file_content:)
90
+ PistePal::DataServices::GpxDoc.set_instance(file_content)
91
+ @trackpoints = PistePal::DataServices::Trackpoints.call
92
+ @date, @resort = PistePal::DataServices::DateAndResort.call
93
+ @maximum_speed, @peak_altitude = PistePal::DataServices::MaxSpeedAndAltitude.call(trackpoints: @trackpoints)
94
+ @runs, @lifts = PistePal::DataServices::RunsAndLifts.call(trackpoints: @trackpoints)
95
+ @distance = PistePal::DataServices::Distance.call(trackpoints: @runs)
96
+ @vertical = PistePal::DataServices::Vertical.call(trackpoints: @runs)
97
+ @tallest_run, @longest_run = PistePal::DataServices::TallestAndLongestRun.call(runs: @runs)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,35 @@
1
+ module PistePal
2
+ class SeasonPass
3
+ attr_accessor :days, :resorts, :runs, :vertical, :distance
4
+
5
+ def self.purchase files
6
+ new(files: files)
7
+ end
8
+
9
+ def days(timestamp_only: false)
10
+ return @days if !timestamp_only
11
+ return @days.map {|day| day.date }
12
+ end
13
+
14
+ private
15
+
16
+ def initialize(files:)
17
+ @days = []
18
+ files.each do |f|
19
+ @days.push(PistePal::DayPass.purchase(f))
20
+ end
21
+
22
+ @resorts = @days.map {|day| day.resort }.uniq
23
+ @runs = @days.map {|day| day.runs.count }.reduce(0, :+)
24
+ @vertical = {
25
+ :value => @days.map {|day| day.vertical[:value] }.reduce(0, :+),
26
+ :unit => @days.first.vertical[:unit]
27
+ }
28
+ @distance = {
29
+ :value => @days.map {|day| day.distance[:value] }.reduce(0, :+),
30
+ :unit => @days.first.distance[:unit]
31
+ }
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,15 @@
1
+ module PistePal
2
+ class Trackpoint
3
+ attr_accessor :lat, :lon, :elevation, :time, :hdop, :vdop, :speed
4
+
5
+ def initialize(lat: nil, lon: nil, elevation: nil, time: nil, hdop: nil, vdop: nil, speed: nil)
6
+ @lat = lat
7
+ @lon = lon
8
+ @elevation = elevation
9
+ @time = time
10
+ @hdop = hdop
11
+ @vdop = vdop
12
+ @speed = speed
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module PistePal
2
+ VERSION = "1.0.0"
3
+ end
data/lib/piste_pal.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "piste_pal/version"
2
+ require "piste_pal/day_pass"
3
+ require "piste_pal/season_pass"
4
+ require "piste_pal/trackpoint"
5
+ require "piste_pal/data_services/gpx_doc"
6
+ require "piste_pal/data_services/trackpoints"
7
+ require "piste_pal/data_services/date_and_resort"
8
+ require "piste_pal/data_services/max_speed_and_altitude"
9
+ require "piste_pal/data_services/distance"
10
+ require "piste_pal/data_services/runs_and_lifts"
11
+ require "piste_pal/data_services/vertical"
12
+ require "piste_pal/data_services/tallest_and_longest_run"
data/piste_pal.gemspec ADDED
@@ -0,0 +1,34 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "piste_pal/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "piste_pal"
8
+ spec.version = PistePal::VERSION
9
+ spec.authors = ["Josh Menden"]
10
+ spec.email = ["josh.menden@gmail.com"]
11
+
12
+ spec.summary = %q{A Ruby Gem that helps calculate Skiing statistics from GPS data.}
13
+ # spec.description = %q{TODO: Write a longer description or delete this line.}
14
+ spec.homepage = "https://github.com/joshmenden/piste_pal"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_development_dependency "bundler", "~> 2.0"
28
+ spec.add_development_dependency "rake", "~> 10.0"
29
+ spec.add_development_dependency "rspec", "~> 3.0"
30
+ spec.add_development_dependency "byebug", "~> 11.0"
31
+ spec.add_dependency "nokogiri", "~> 1.10"
32
+ spec.add_dependency "rake", "~> 10.0"
33
+ spec.add_dependency "vincenty_distance", "~> 1.0.0"
34
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: piste_pal
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Menden
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-08-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '11.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '11.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.10'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.10'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: vincenty_distance
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.0.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.0.0
111
+ description:
112
+ email:
113
+ - josh.menden@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - ".travis.yml"
121
+ - Gemfile
122
+ - Gemfile.lock
123
+ - LICENSE.txt
124
+ - README.md
125
+ - Rakefile
126
+ - bin/console
127
+ - bin/setup
128
+ - lib/piste_pal.rb
129
+ - lib/piste_pal/data_services/date_and_resort.rb
130
+ - lib/piste_pal/data_services/distance.rb
131
+ - lib/piste_pal/data_services/gpx_doc.rb
132
+ - lib/piste_pal/data_services/max_speed_and_altitude.rb
133
+ - lib/piste_pal/data_services/runs_and_lifts.rb
134
+ - lib/piste_pal/data_services/tallest_and_longest_run.rb
135
+ - lib/piste_pal/data_services/trackpoints.rb
136
+ - lib/piste_pal/data_services/vertical.rb
137
+ - lib/piste_pal/day_pass.rb
138
+ - lib/piste_pal/season_pass.rb
139
+ - lib/piste_pal/trackpoint.rb
140
+ - lib/piste_pal/version.rb
141
+ - piste_pal.gemspec
142
+ homepage: https://github.com/joshmenden/piste_pal
143
+ licenses:
144
+ - MIT
145
+ metadata: {}
146
+ post_install_message:
147
+ rdoc_options: []
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubyforge_project:
162
+ rubygems_version: 2.7.6
163
+ signing_key:
164
+ specification_version: 4
165
+ summary: A Ruby Gem that helps calculate Skiing statistics from GPS data.
166
+ test_files: []