tallakt-gpsspeed 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/LICENSE +24 -0
- data/Manifest.txt +17 -0
- data/PostInstall.txt +7 -0
- data/README.rdoc +92 -0
- data/Rakefile +29 -0
- data/bin/gpsspeed +4 -0
- data/gpsspeed.gemspec +44 -0
- data/lib/gpsspeed.rb +6 -0
- data/lib/gpsspeed/runner.rb +159 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/input/test_walk.gpx +121 -0
- data/test/input/tracks.gpx +3964 -0
- data/test/test_gpsspeed.rb +11 -0
- data/test/test_helper.rb +3 -0
- metadata +104 -0
data/History.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
The MIT License
|
4
|
+
|
5
|
+
Copyright (c) 2009 Tallak Tveide
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
9
|
+
in the Software without restriction, including without limitation the rights
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
12
|
+
furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be included in
|
15
|
+
all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
THE SOFTWARE.
|
24
|
+
|
data/Manifest.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
bin/gpsspeed
|
2
|
+
gpsspeed.gemspec
|
3
|
+
History.txt
|
4
|
+
lib/gpsspeed/runner.rb
|
5
|
+
lib/gpsspeed.rb
|
6
|
+
LICENSE
|
7
|
+
Manifest.txt
|
8
|
+
PostInstall.txt
|
9
|
+
Rakefile
|
10
|
+
README.rdoc
|
11
|
+
script/console
|
12
|
+
script/generate
|
13
|
+
script/destroy
|
14
|
+
test/test_helper.rb
|
15
|
+
test/test_gpsspeed.rb
|
16
|
+
test/input/test_walk.gpx
|
17
|
+
test/input/tracks.gpx
|
data/PostInstall.txt
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
= gpsspeed
|
2
|
+
|
3
|
+
http://github.com/tallakt/plcutil
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
GPS Track speed calculation utility
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
This utility will take a gpx file as input (created for instance by http://www.easygps.com/)
|
12
|
+
and display the statistics for the best speeds of the tracks in the file over a certain
|
13
|
+
distance that may be supplied
|
14
|
+
|
15
|
+
== SYNOPSIS:
|
16
|
+
|
17
|
+
A starting point for using gpsspeed, try the --help option
|
18
|
+
|
19
|
+
gpsspeed --help
|
20
|
+
|
21
|
+
Usage: gpsspeed [options] <input.gpx>
|
22
|
+
-l, --length DIST Minimum required length (default 500m)
|
23
|
+
Accepts km,m,ft,miles,nm
|
24
|
+
-s, --speed-format FMT Select speed format (default m/s)
|
25
|
+
Accepts knots,mph,m/s,km/h
|
26
|
+
-f, --length-format FMT Select length format (default m)
|
27
|
+
Accepts km,m,ft,miles,nm
|
28
|
+
-h, --help Show this message
|
29
|
+
|
30
|
+
|
31
|
+
A common usage would be to run the command without any special options
|
32
|
+
|
33
|
+
gpsspeed myfile.gpx
|
34
|
+
|
35
|
+
Which gives you an output like this
|
36
|
+
|
37
|
+
Speed [m/s] #frm #to #n time [s] Distance [m]
|
38
|
+
1.6411 4 9 6 65.00 106.7
|
39
|
+
1.4037 13 19 7 77.00 108.1
|
40
|
+
|
41
|
+
The utility finds the highest speeds in the track file over a
|
42
|
+
minimum distance. The best runs are listed in descending order
|
43
|
+
(ie. fastest run first). To use a different length, use the -l
|
44
|
+
parameter
|
45
|
+
|
46
|
+
gpsspeed -l 200m myfile.gpx
|
47
|
+
|
48
|
+
To display the results in different units, use the -s and -f
|
49
|
+
options, like this
|
50
|
+
|
51
|
+
gpsspeed -s knots -f nm
|
52
|
+
|
53
|
+
Which gives you a result table like this
|
54
|
+
|
55
|
+
Speed [knots] #frm #to #n time [s] Distance [nm]
|
56
|
+
3.1901 4 9 6 65.00 0.0576
|
57
|
+
2.7285 13 19 7 77.00 0.0584
|
58
|
+
|
59
|
+
|
60
|
+
== REQUIREMENTS:
|
61
|
+
|
62
|
+
Requires the gem tallakt-geoutm
|
63
|
+
|
64
|
+
== INSTALL:
|
65
|
+
|
66
|
+
sudo gem sources -a http://gems.github.com
|
67
|
+
sudo gem install tallakt-gpsspeed
|
68
|
+
|
69
|
+
== LICENSE:
|
70
|
+
|
71
|
+
(The MIT License)
|
72
|
+
|
73
|
+
Copyright (c) 2009 Tallak Tveide
|
74
|
+
|
75
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
76
|
+
a copy of this software and associated documentation files (the
|
77
|
+
'Software'), to deal in the Software without restriction, including
|
78
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
79
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
80
|
+
permit persons to whom the Software is furnished to do so, subject to
|
81
|
+
the following conditions:
|
82
|
+
|
83
|
+
The above copyright notice and this permission notice shall be
|
84
|
+
included in all copies or substantial portions of the Software.
|
85
|
+
|
86
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
87
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
88
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
89
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
90
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
91
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
92
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
|
2
|
+
require File.dirname(__FILE__) + '/lib/gpsspeed'
|
3
|
+
require 'geoutm'
|
4
|
+
|
5
|
+
# Generate all the Rake tasks
|
6
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
7
|
+
$hoe = Hoe.new('gpsspeed', Gpsspeed::VERSION) do |p|
|
8
|
+
p.developer('Tallak Tveide', 'tallak@tveide.net')
|
9
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
10
|
+
p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
11
|
+
p.rubyforge_name = p.name # TODO this is default value
|
12
|
+
p.extra_deps = [
|
13
|
+
['tallakt-geoutm',">= #{::GeoUtm::VERSION}"],
|
14
|
+
]
|
15
|
+
p.extra_dev_deps = [
|
16
|
+
['newgem', ">= #{::Newgem::VERSION}"]
|
17
|
+
]
|
18
|
+
|
19
|
+
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
20
|
+
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
21
|
+
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
22
|
+
p.rsync_args = '-av --delete --ignore-errors'
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'newgem/tasks' # load /tasks/*.rake
|
26
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
27
|
+
|
28
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
29
|
+
# task :default => [:spec, :features]
|
data/bin/gpsspeed
ADDED
data/gpsspeed.gemspec
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{gpsspeed}
|
5
|
+
s.version = "0.0.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Tallak Tveide"]
|
9
|
+
s.date = %q{2009-02-16}
|
10
|
+
s.default_executable = %q{gpsspeed}
|
11
|
+
s.description = %q{GPS Track speed calculation utility}
|
12
|
+
s.email = ["tallak@tveide.net"]
|
13
|
+
s.executables = ["gpsspeed"]
|
14
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc"]
|
15
|
+
s.files = ["bin/gpsspeed", "gpsspeed.gemspec", "History.txt", "lib/gpsspeed/runner.rb", "lib/gpsspeed.rb", "LICENSE", "Manifest.txt", "PostInstall.txt", "Rakefile", "README.rdoc", "script/console", "script/generate", "script/destroy", "test/test_helper.rb", "test/test_gpsspeed.rb", "test/input/test_walk.gpx", "test/input/tracks.gpx"]
|
16
|
+
s.has_rdoc = true
|
17
|
+
s.homepage = %q{http://github.com/tallakt/plcutil}
|
18
|
+
s.post_install_message = %q{PostInstall.txt}
|
19
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.rubyforge_project = %q{gpsspeed}
|
22
|
+
s.rubygems_version = %q{1.3.1}
|
23
|
+
s.summary = %q{GPS Track speed calculation utility}
|
24
|
+
s.test_files = ["test/test_helper.rb", "test/test_gpsspeed.rb"]
|
25
|
+
|
26
|
+
if s.respond_to? :specification_version then
|
27
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
28
|
+
s.specification_version = 2
|
29
|
+
|
30
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
31
|
+
s.add_runtime_dependency(%q<tallakt-geoutm>, [">= 0.0.3"])
|
32
|
+
s.add_development_dependency(%q<newgem>, [">= 1.2.3"])
|
33
|
+
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
|
34
|
+
else
|
35
|
+
s.add_dependency(%q<tallakt-geoutm>, [">= 0.0.3"])
|
36
|
+
s.add_dependency(%q<newgem>, [">= 1.2.3"])
|
37
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
38
|
+
end
|
39
|
+
else
|
40
|
+
s.add_dependency(%q<tallakt-geoutm>, [">= 0.0.3"])
|
41
|
+
s.add_dependency(%q<newgem>, [">= 1.2.3"])
|
42
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
43
|
+
end
|
44
|
+
end
|
data/lib/gpsspeed.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'geoutm'
|
4
|
+
require 'enumerator'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
class FloatA # necessary dt rexml bug
|
8
|
+
def each
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module GPSSpeed
|
13
|
+
class Runner
|
14
|
+
DEFAULT_LENGTH = 500
|
15
|
+
DEFAULT_SPEED_FMT = 'm/s'
|
16
|
+
DEFAULT_LENGTH_FMT = 'm'
|
17
|
+
|
18
|
+
LengthUnits = {
|
19
|
+
'm' => 1.0,
|
20
|
+
'ft' => 3.2808399,
|
21
|
+
'km' => 0.001,
|
22
|
+
'miles' => 0.000621371192,
|
23
|
+
'nm' => 0.000539956803
|
24
|
+
}
|
25
|
+
|
26
|
+
SpeedUnits = {
|
27
|
+
'm/s' => 1.0,
|
28
|
+
'knots' => 1.9438612860586,
|
29
|
+
'km/h' => 3.6,
|
30
|
+
'mph' => 2.23693629
|
31
|
+
}
|
32
|
+
|
33
|
+
def initialize(argv)
|
34
|
+
# Command line options
|
35
|
+
@opt = {}
|
36
|
+
parse_options argv
|
37
|
+
@opt[:length] ||= DEFAULT_LENGTH # Minimum required distance
|
38
|
+
@opt[:speed_fmt] ||= DEFAULT_SPEED_FMT
|
39
|
+
@opt[:length_fmt] ||= DEFAULT_LENGTH_FMT
|
40
|
+
|
41
|
+
filename, = argv
|
42
|
+
|
43
|
+
read_gpx filename
|
44
|
+
list_distance_speeds
|
45
|
+
filter_distance_speeds
|
46
|
+
print_results
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_length(l)
|
50
|
+
LengthUnits.each do |name, factor|
|
51
|
+
m = l.match /^(\d+(.\d+)?)\s?#{name}$/i
|
52
|
+
return m[1].to_f * factor if m
|
53
|
+
end
|
54
|
+
m = l.match /^(\d+(.\d+)?)$/i
|
55
|
+
throw RuntimeError.new('Illegal length format: ' + l) unless m
|
56
|
+
m[1].to_f
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_options(argv)
|
60
|
+
OptionParser.new do |op|
|
61
|
+
op.banner = "Usage: gpsspeed [options] <input.gpx>"
|
62
|
+
op.on("-l", "--length DIST", "Minimum required length (default #{DEFAULT_LENGTH}m)",
|
63
|
+
" Accepts #{LengthUnits.keys.join ','}") do |l|
|
64
|
+
@opt[:length] = parse_length l
|
65
|
+
end
|
66
|
+
op.on("-s", "--speed-format FMT", "Select speed format (default #{DEFAULT_SPEED_FMT})",
|
67
|
+
" Accepts #{SpeedUnits.keys.join ','}") do |fmt|
|
68
|
+
@opt[:speed_fmt] = fmt
|
69
|
+
end
|
70
|
+
op.on("-f", "--length-format FMT", "Select length format (default #{DEFAULT_LENGTH_FMT})",
|
71
|
+
" Accepts #{LengthUnits.keys.join ','}") do |fmt|
|
72
|
+
@opt[:length_fmt] = fmt
|
73
|
+
end
|
74
|
+
op.on_tail("-h", "--help", "Show this message") do
|
75
|
+
puts op
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
end.parse! argv
|
79
|
+
end
|
80
|
+
|
81
|
+
def read_gpx filename
|
82
|
+
throw RuntimeError.new("Please specify file name") unless filename
|
83
|
+
doc = nil
|
84
|
+
File.open filename do |f|
|
85
|
+
doc = REXML::Document.new f
|
86
|
+
end
|
87
|
+
|
88
|
+
@points = []
|
89
|
+
REXML::XPath.each doc, '//trkpt' do |trkpt|
|
90
|
+
lat = trkpt.attributes['lat']
|
91
|
+
lon = trkpt.attributes['lon']
|
92
|
+
date_time = DateTime.strptime trkpt.get_elements('time').first.text
|
93
|
+
pp = OpenStruct.new
|
94
|
+
pp.latlon = GeoUtm::LatLon.new lat.to_f, lon.to_f
|
95
|
+
pp.utm = pp.latlon.to_utm GeoUtm::Ellipsoid::lookup(:wgs84), @points.first && @points.first.utm.zone
|
96
|
+
pp.date_time = date_time
|
97
|
+
@points << pp
|
98
|
+
end
|
99
|
+
@points.sort! {|a, b| a.date_time <=> b.date_time }
|
100
|
+
# puts "Read #{@points.size} track points"
|
101
|
+
end
|
102
|
+
|
103
|
+
def list_distance_speeds
|
104
|
+
@dist_speed_list = []
|
105
|
+
(0..(@points.size - 2)).each do |i|
|
106
|
+
((i + 1)..(@points.size - 1)).each do |j|
|
107
|
+
dist = @points[i].utm.distance_to @points[j].utm
|
108
|
+
next if dist < @opt[:length]
|
109
|
+
ds = OpenStruct.new
|
110
|
+
ds.distance = dist
|
111
|
+
ds.begin = i
|
112
|
+
ds.end = j
|
113
|
+
ds.dt = (@points[j].date_time - @points[i].date_time) * 24.0 * 60.0 * 60.0
|
114
|
+
ds.speed_ms = dist / ds.dt
|
115
|
+
@dist_speed_list << ds
|
116
|
+
break
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def filter_distance_speeds
|
122
|
+
@dist_speed_list.sort! {|a, b| a.speed_ms <=> b.speed_ms }.reverse!
|
123
|
+
@dist_speed_list.reject! do |candidate|
|
124
|
+
intersects_previous candidate
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def intersects_previous candidate
|
129
|
+
intersects = nil
|
130
|
+
@dist_speed_list[0...@dist_speed_list.index(candidate)].each do |prev|
|
131
|
+
used = prev.begin..prev.end
|
132
|
+
intersects ||= ((prev.begin..prev.end).to_a & (candidate.begin..candidate.end).to_a).any?
|
133
|
+
break if intersects
|
134
|
+
end
|
135
|
+
intersects
|
136
|
+
end
|
137
|
+
|
138
|
+
def print_results
|
139
|
+
puts '%20s%10s%10s%10s%20s%20s' %
|
140
|
+
["Speed [#{@opt[:speed_fmt]}]", '#frm', '#to', '#n', 'time [s]', "Distance [#{@opt[:length_fmt]}]"]
|
141
|
+
(@dist_speed_list[0..39] || []).each do |ds|
|
142
|
+
puts "%20.4f%10i%10i%10i%20.2f#{length_printf_fmt(20)}" % [speed_conv(ds.speed_ms), ds.begin,
|
143
|
+
ds.end, ds.end - ds.begin + 1, ds.dt, length_conv(ds.distance)]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def speed_conv(speed)
|
148
|
+
speed * SpeedUnits[@opt[:speed_fmt]]
|
149
|
+
end
|
150
|
+
|
151
|
+
def length_conv(length)
|
152
|
+
length * LengthUnits[@opt[:length_fmt]]
|
153
|
+
end
|
154
|
+
|
155
|
+
def length_printf_fmt(width)
|
156
|
+
"%#{width}.#{-Math::log10(LengthUnits[@opt[:length_fmt]]).to_i + 1}f"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/gpsspeed.rb'}"
|
9
|
+
puts "Loading gpsspeed gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|