gps2map 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3cc4350e15d0c62b55fb807c3a703fb0962d70f5077a6f016fe4cc5bee6be5f2
4
+ data.tar.gz: b5c53b6993f76aa8ed70fdeabc299afa03d1983520116ab8631e23cb889a2c83
5
+ SHA512:
6
+ metadata.gz: a3993f4856b452e7bfdc2d19eebf8a02a5c958c5c35a7fda2efb7e3d00ffcf2b26448528c17da9973474b1a41e44e376555357b62beca4fc4e3354a4c9d79679
7
+ data.tar.gz: f801917602ccee3872e8cff9862a65f60c8068f309a885c2b2e66c8a4bbe80084f73c01ce80304c6edf7ce1b043467d1e81166f2d232a44ef178d5c837b66328
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # GPS2Map
2
+ Creates a Google Map from an input file containing GPS co-ordinates ([GPX](https://en.wikipedia.org/wiki/GPS_Exchange_Format), [KML](https://en.wikipedia.org/wiki/Keyhole_Markup_Language) or [TCX](https://en.wikipedia.org/wiki/Training_Center_XML) format).
3
+
4
+ ## Installation
5
+ ```
6
+ $ gem install gps2map
7
+ ```
8
+
9
+ ## Prerequisites
10
+ A [Google Maps JavaScript API key](https://developers.google.com/maps/documentation/javascript/get-api-key) is required and must be specified in an environment variable named `GOOGLE_MAPS_API_KEY`.
11
+
12
+ ## Usage
13
+ ```
14
+ gps2map generate <input.gpx|.kml|.tcx> <output.html>
15
+ ```
16
+
17
+ ## Licence
18
+ The gem is available as open source under the terms of the [MIT Licence](https://opensource.org/licenses/MIT).
19
+
20
+ ## Copyright
21
+ Copyright (C) 2018 John Topley.
data/bin/gps2map ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'gps2map'
5
+
6
+ GPS2Map::GPS2Map.start(ARGV)
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Open up the Integer class to add a formatting method.
4
+ class Integer
5
+ def to_comma_formatted
6
+ to_s.gsub(/(\d)(?=(\d\d\d)+(?!\d))/, '\\1,')
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GPS2Map
4
+ VERSION = '1.0.0'
5
+ end
data/lib/gps2map.rb ADDED
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nokogiri'
4
+ require 'open-uri'
5
+ require 'ostruct'
6
+ require 'thor'
7
+ require 'erb'
8
+ require 'csv'
9
+
10
+ require_relative 'core_ext/integer'
11
+ require_relative 'gps2map/version'
12
+
13
+ # Namespace all the gem code.
14
+ module GPS2Map
15
+ DIGITS = 6
16
+ LATITUDE_OFFSET = 0.005000
17
+ MARKERS = 15
18
+
19
+ Coordinate = Struct.new(:latitude, :longitude)
20
+ Marker = Struct.new(:latitude, :longitude, :label)
21
+
22
+ # Class that takes an input file containing GPS co-ordinates and outputs an HTML file containing a
23
+ # Google Map of those co-ordinates.
24
+ class GPS2Map < Thor
25
+ desc 'generate <input.gpx|.kml|.tcx> <output.html>', 'Generates a Google Map from an input file'
26
+ option :markers, type: :numeric, default: MARKERS, desc: 'The number of markers to plot on the map'
27
+ def generate(input_file, output_file)
28
+ vars = {}
29
+ vars[:api_key] = ENV['GOOGLE_MAPS_API_KEY'] || abort('No GOOGLE_MAPS_API_KEY provided.')
30
+ vars[:title] = input_file
31
+ coordinates = []
32
+ markers = []
33
+ label = 0
34
+ latitudes, longitudes = parse_file(input_file)
35
+ interval = latitudes.length / options[:markers]
36
+
37
+ latitudes.each_with_index do |latitude, i|
38
+ longitude = longitudes[i]
39
+ coordinates << Coordinate.new(latitude, longitude)
40
+ markers << Marker.new(latitude, longitude, label += 1) if i > 1 && (i % interval).zero?
41
+ end
42
+
43
+ vars[:coordinates] = coordinates
44
+ vars[:markers] = markers
45
+ vars[:map_centre_lat] = mean_of(coordinates.map(&:latitude)) + LATITUDE_OFFSET
46
+ vars[:map_centre_long] = mean_of(coordinates.map(&:longitude))
47
+ vars[:map_start_lat] = coordinates.first.latitude
48
+ vars[:map_start_long] = coordinates.first.longitude
49
+ write_output_file(output_file, execute_template(vars))
50
+ puts "Plotted #{latitudes.length.to_comma_formatted} co-ordinates with #{markers.length} markers."
51
+ end
52
+
53
+ private
54
+
55
+ def execute_template(variables)
56
+ template = File.join(__dir__, 'map.erb')
57
+ context = OpenStruct.new(variables).instance_eval { binding }
58
+ # Set the ERB trim mode to suppress newlines.
59
+ ERB.new(File.read(template), 0, '>').result(context)
60
+ end
61
+
62
+ def mean_of(coordinates)
63
+ coordinates.sum.fdiv(coordinates.length).round(DIGITS)
64
+ end
65
+
66
+ def parse_file(input_file)
67
+ case File.extname(input_file).downcase
68
+ when '.gpx'
69
+ parse_gpx(input_file)
70
+ when '.kml'
71
+ parse_kml(input_file)
72
+ when '.tcx'
73
+ parse_tcx(input_file)
74
+ end
75
+ end
76
+
77
+ def parse(input_file, selector)
78
+ File.open(input_file, 'r') do |fi|
79
+ doc = Nokogiri::XML(fi)
80
+ doc.css(selector).each { |el| yield(el) }
81
+ end
82
+ end
83
+
84
+ def parse_gpx(input_file)
85
+ latitudes = []
86
+ longitudes = []
87
+ parse(input_file, 'trkpt') do |el|
88
+ latitudes << el['lat'].to_f.round(DIGITS)
89
+ longitudes << el['lon'].to_f.round(DIGITS)
90
+ end
91
+ [latitudes, longitudes]
92
+ end
93
+
94
+ def parse_kml(input_file)
95
+ latitudes = []
96
+ longitudes = []
97
+ parse(input_file, 'Point coordinates') do |el|
98
+ # KML has longitude first.
99
+ longitudes << CSV.parse(el.text.strip).first.first.to_f.round(DIGITS)
100
+ latitudes << CSV.parse(el.text.strip).first[1].to_f.round(DIGITS)
101
+ end
102
+ [latitudes, longitudes]
103
+ end
104
+
105
+ def parse_tcx(input_file)
106
+ latitudes = []
107
+ longitudes = []
108
+ File.open(input_file, 'r') do |fi|
109
+ doc = Nokogiri::XML(fi)
110
+ doc.css('LatitudeDegrees').each { |el| latitudes << el.text.to_f.round(DIGITS) }
111
+ doc.css('LongitudeDegrees').each { |el| longitudes << el.text.to_f.round(DIGITS) }
112
+ end
113
+ [latitudes, longitudes]
114
+ end
115
+
116
+ def write_output_file(output_file, data)
117
+ File.open(output_file, 'w') { |fi| fi.write(data) }
118
+ end
119
+ end
120
+ end
data/lib/map.erb ADDED
@@ -0,0 +1,59 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= title %></title>
5
+ <meta name="viewport" content="initial-scale=1.0">
6
+ <meta charset="utf-8">
7
+ <style>
8
+ /* Always set the map height explicitly to define the size of the div
9
+ * element that contains the map. */
10
+ #map {
11
+ height: 100%;
12
+ }
13
+ /* Optional: Makes the sample page fill the window. */
14
+ html, body {
15
+ height: 100%;
16
+ margin: 0;
17
+ padding: 0;
18
+ }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <div id="map"></div>
23
+ <script>
24
+ var map;
25
+ function initMap() {
26
+ map = new google.maps.Map(document.getElementById('map'), {
27
+ center: { lat: <%= map_centre_lat %>, lng: <%= map_centre_long %> },
28
+ zoom: 12
29
+ });
30
+ var coordinates = [
31
+ <% coordinates.each do |coordinate| %>
32
+ { lat: <%= coordinate.latitude %>, lng: <%= coordinate.longitude %> },
33
+ <% end %>
34
+ ];
35
+ var polyline = new google.maps.Polyline({
36
+ path: coordinates,
37
+ geodesic: true,
38
+ strokeColor: '#0088FF',
39
+ strokeOpacity: 0.6,
40
+ strokeWeight: 6
41
+ });
42
+ polyline.setMap(map);
43
+ var flagMarker = new google.maps.Marker({
44
+ position: { lat: <%= map_start_lat %>, lng: <%= map_start_long %>},
45
+ map: map,
46
+ icon: 'https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png'
47
+ });
48
+ <% markers.each do |marker| %>
49
+ new google.maps.Marker({
50
+ position: { lat: <%= marker.latitude %>, lng: <%= marker.longitude %> },
51
+ map: map,
52
+ label: '<%= marker.label %>'
53
+ });
54
+ <% end %>
55
+ }
56
+ </script>
57
+ <script src="https://maps.googleapis.com/maps/api/js?key=<%= api_key %>&callback=initMap" async defer></script>
58
+ </body>
59
+ </html>
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gps2map
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - John Topley
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.8.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.8.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.20.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.20.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.16'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.16'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.49.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.49.1
83
+ description: 'Creates a Google Map from an input file containing GPS co-ordinates
84
+ (GPX, KML or TCX format).
85
+
86
+ '
87
+ email: john@johntopley.com
88
+ executables:
89
+ - gps2map
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - README.md
94
+ - bin/gps2map
95
+ - lib/core_ext/integer.rb
96
+ - lib/gps2map.rb
97
+ - lib/gps2map/version.rb
98
+ - lib/map.erb
99
+ homepage: https://github.com/johntopley/gps2map
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.7.5
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: Creates a Google Map from an input file
123
+ test_files: []