gps2map 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: 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: []