gpx 1.0.0 → 1.1.1

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +37 -0
  3. data/.rubocop +1 -0
  4. data/.rubocop.yml +2 -5
  5. data/.ruby-version +1 -0
  6. data/.tool-versions +1 -0
  7. data/.travis.yml +4 -5
  8. data/CHANGELOG.md +14 -0
  9. data/Gemfile +2 -0
  10. data/README.md +21 -5
  11. data/Rakefile +7 -0
  12. data/UPGRADING.md +7 -0
  13. data/bin/gpx_distance +2 -0
  14. data/bin/gpx_smooth +5 -2
  15. data/gpx.gemspec +2 -2
  16. data/lib/gpx/bounds.rb +3 -0
  17. data/lib/gpx/geo_json.rb +199 -0
  18. data/lib/gpx/gpx.rb +2 -0
  19. data/lib/gpx/gpx_file.rb +53 -51
  20. data/lib/gpx/magellan_track_log.rb +3 -1
  21. data/lib/gpx/point.rb +5 -1
  22. data/lib/gpx/route.rb +4 -1
  23. data/lib/gpx/segment.rb +14 -9
  24. data/lib/gpx/track.rb +11 -4
  25. data/lib/gpx/track_point.rb +2 -0
  26. data/lib/gpx/version.rb +3 -1
  27. data/lib/gpx/waypoint.rb +3 -0
  28. data/lib/gpx.rb +3 -1
  29. data/tests/geojson_files/combined_data.json +68 -0
  30. data/tests/geojson_files/line_string_data.json +83 -0
  31. data/tests/geojson_files/multi_line_string_data.json +74 -0
  32. data/tests/geojson_files/multi_point_data.json +14 -0
  33. data/tests/geojson_files/point_data.json +22 -0
  34. data/tests/geojson_test.rb +92 -0
  35. data/tests/gpx10_test.rb +2 -0
  36. data/tests/gpx_file_test.rb +2 -0
  37. data/tests/gpx_files/one_segment_mixed_times.gpx +884 -0
  38. data/tests/gpx_files/routes_without_names.gpx +29 -0
  39. data/tests/magellan_test.rb +2 -0
  40. data/tests/output_test.rb +3 -1
  41. data/tests/route_test.rb +52 -0
  42. data/tests/segment_test.rb +13 -1
  43. data/tests/track_file_test.rb +3 -0
  44. data/tests/track_point_test.rb +2 -0
  45. data/tests/track_test.rb +2 -0
  46. data/tests/waypoint_test.rb +2 -0
  47. metadata +25 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4aa764ea01fcc4255a69ff9bad6d1c159fc8ae8a1b29c0e54d05b9fe2b0e0bc9
4
- data.tar.gz: 699edd3b9dec8cc83ac136c90b81f428a60ed8359aa01445ef3aead1a343c0d1
3
+ metadata.gz: b894a8f06a91646472aa6311602be3da1c0d4767a7bc44867ba954ecf93d1909
4
+ data.tar.gz: 73b5b4e73fb783f3e10a007e2101f49db0e57f48e3675fea7b8255d1a21b84ad
5
5
  SHA512:
6
- metadata.gz: 43dc778d82e3b044ef872c219a60a703a6e4e53845817e783c464b36b37b60647eb5c390e2b16df1f1bb51b5359d2eae5a05e39fdb352ee3e2eaec179e007b9e
7
- data.tar.gz: a6aa72c84b2ff150569506a27889835b818057a549a72d44ad163e81ef5000dd936ca91b8da7faf3a44150ec66bdbe6bb1630291dbeeca5c3416f676130c368a
6
+ metadata.gz: 279dff5711fc5808537e5e69d9be696dc9439b899021f5ab3a7532a8c68d5c099a40b63fefd89d7ebba1c64c250e599211f4a157028d4f00663b41a77cbf4ae6
7
+ data.tar.gz: b0c69fe2fa5014edb2e3628fc73b949b1d9fdaad5e73ab7546e7fd74d47c295be5722451cb8cd7cec5ebeb7ba85cc32e9ac38682d9b72b6e223f0b3295e594a1
@@ -0,0 +1,37 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+ workflow_dispatch:
16
+
17
+ jobs:
18
+ test:
19
+
20
+ runs-on: ubuntu-latest
21
+ strategy:
22
+ fail-fast: true
23
+ matrix:
24
+ ruby-version: ['2.7', '3.0', '3.1', '3.2']
25
+
26
+ steps:
27
+ - uses: actions/checkout@v2
28
+ - name: Set up Ruby
29
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
30
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
31
+ # uses: ruby/setup-ruby@v1
32
+ uses: ruby/setup-ruby@v1.149.0
33
+ with:
34
+ ruby-version: ${{ matrix.ruby-version }}
35
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
36
+ - name: Run tests
37
+ run: bundle exec rake
data/.rubocop ADDED
@@ -0,0 +1 @@
1
+ --server
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.2
2
+ TargetRubyVersion: 2.7
3
3
 
4
4
  Style/Alias:
5
5
  Enabled: false
@@ -152,7 +152,7 @@ Metrics/ParameterLists:
152
152
  Metrics/PerceivedComplexity:
153
153
  Enabled: false
154
154
 
155
- Naming/UncommunicativeMethodParamName:
155
+ Naming/MethodParameterName:
156
156
  Enabled: false
157
157
 
158
158
  Naming/VariableNumber:
@@ -160,6 +160,3 @@ Naming/VariableNumber:
160
160
 
161
161
  Style/DateTime:
162
162
  Enabled: false
163
-
164
- Performance/Caller:
165
- Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.2.2
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.2.2
data/.travis.yml CHANGED
@@ -1,7 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2
4
- - 2.3
5
- - 2.4
6
- - 2.5
7
- script: "bundle exec rake ci:build"
3
+ - 2.7
4
+ - 3.0
5
+ - 3.1
6
+ script: "bundle exec rake"
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.1.1] - 2023-05-19
2
+
3
+ * updates CI, minimal Ruby version now 2.7, updates tooling like rubocop and GitHub actions
4
+ * adds support for Ruby 3.2
5
+ * adds UPGRADING.md to document changes between versions
6
+
7
+ ## [1.1.0] - 2023-05-18
8
+ * Specify UTF-8 encoding for XML encoding (#35 via @sh1nduu)
9
+ * Added GeoJSON conversion (#38 via @tyrauber and @niborg)
10
+ * Support Ruby 3 (#43 via @LocoDelAssembly)
11
+ * Fix nil-to-Time comparison (#46 via @frodrigo)
12
+ * Fix bug when <rte> GPX file does not specify <name> tag (#41 via @niborg)
13
+ * Drop Ruby 2.5 and 2.6 from CI (#50 via @niborg)
14
+
1
15
  ## [1.0.0] - 2018-03-06
2
16
 
3
17
  * Fix duplication of points on appending segment to track (#20 via @niborg)
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in seryz.gemspec
data/README.md CHANGED
@@ -10,15 +10,20 @@ the data as objects. For more info on the GPX format, see
10
10
  http://www.topografix.com/gpx.asp.
11
11
 
12
12
  In addition to parsing GPX files, this library is capable of converting
13
- Magellan NMEA files to GPX, and writing new GPX files. It can crop and delete
14
- rectangular areas within a file, and it also calculates some meta-data about
15
- the tracks and points in a file (such as distance, duration, average speed,
16
- etc).
13
+ Magellan NMEA files to GPX, converting GeoJSON data to GPX, and writing
14
+ new GPX files. It can crop and delete rectangular areas within a file,
15
+ and it also calculates some meta-data about the tracks and points in a file (such as distance, duration, average speed, etc).
17
16
 
18
17
  ## Requirements
19
18
 
20
- As of `1.0.0`, `gpx` requires at least Ruby 2.2 to run.
19
+ - As of `1.1.1`, `gpx` requires at least Ruby 2.7 to run.
20
+ - As of `1.0.0`, `gpx` requires at least Ruby 2.2 to run.
21
21
 
22
+ ## Installation
23
+ Add to your gemfile:
24
+ ```
25
+ gem 'gpx'
26
+ ```
22
27
  ## Examples
23
28
 
24
29
  Reading a GPX file, and cropping its contents to a given area:
@@ -37,6 +42,17 @@ if GPX::MagellanTrackLog::is_magellan_file?(filename)
37
42
  end
38
43
  ```
39
44
 
45
+ Converting GeoJSON data to GPX can be achieved by providing a
46
+ file path, file, or the data in string format:
47
+ ```ruby
48
+ # Converting from a file name
49
+ gpx_file = GPX::GeoJSON.convert_to_gpx(geojson_file: 'mygeojsonfile.json')
50
+
51
+ # Converting from a string
52
+ data = JSON.generate(my_geojson_hash)
53
+ gpx_file = GPX::GeoJSON.convert_to_gpx(geojson_data: data)
54
+ ```
55
+
40
56
  Exporting an ActiveRecord to GPXFile (as Waypoints)
41
57
  ```ruby
42
58
  #
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rake/testtask'
3
5
  require 'rdoc/task'
@@ -32,3 +34,8 @@ Rake::RDocTask.new('doc') do |rdoc|
32
34
  rdoc.rdoc_files.include('README')
33
35
  rdoc.rdoc_files.include('lib/**/*.rb')
34
36
  end
37
+
38
+ desc 'open an irb session preloaded with this gem'
39
+ task :console do
40
+ sh 'irb -r pp -r ./lib/gpx.rb'
41
+ end
data/UPGRADING.md ADDED
@@ -0,0 +1,7 @@
1
+ # Upgrading
2
+
3
+ You will find all the information you need to upgrade from one version to another here.
4
+
5
+ ## Version 1.0.0 to 1.1.1
6
+
7
+ Please make sure, that you need at least Ruby 2.7.0 to use this gem now. As support for Ruby versions below 2.7.0 has been dropped.
data/bin/gpx_distance CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # frozen_string_literal: true
4
+
3
5
  require File.expand_path('../lib/gpx', __dir__)
4
6
 
5
7
  filename = ARGV[0]
data/bin/gpx_smooth CHANGED
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # frozen_string_literal: true
4
+
3
5
  require File.expand_path('../lib/gpx', __dir__)
4
6
  require 'optparse'
5
7
 
6
8
  def str_to_int_or_time(str)
7
- if str =~ /\A\d{10}\Z/
9
+ case str
10
+ when /\A\d{10}\Z/
8
11
  Time.at(str.to_i)
9
- elsif str =~ /\A\d+\Z/
12
+ when /\A\d+\Z/
10
13
  str.to_i
11
14
  else
12
15
  DateTime.strptime(str, '%Y%m%d-%H:%M:%S').to_time
data/gpx.gemspec CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
@@ -11,12 +12,11 @@ Gem::Specification.new do |s|
11
12
  s.summary = 'A basic API for reading and writing GPX files.'
12
13
  s.description = 'A basic API for reading and writing GPX files.'
13
14
 
14
- s.required_ruby_version = '~>2.2'
15
+ s.required_ruby_version = '>= 2.7', '< 4'
15
16
 
16
17
  s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
18
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
19
  s.require_paths = ['lib']
19
- s.has_rdoc = true
20
20
 
21
21
  s.homepage = 'http://www.github.com/dougfales/gpx'
22
22
  s.add_dependency 'nokogiri', '~>1.7'
data/lib/gpx/bounds.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GPX
2
4
  class Bounds < Base
3
5
  attr_accessor :min_lat, :max_lat, :max_lon, :min_lon
@@ -5,6 +7,7 @@ module GPX
5
7
  # Creates a new bounds object with the passed-in min and max longitudes
6
8
  # and latitudes.
7
9
  def initialize(opts = { min_lat: 90.0, max_lat: -90.0, min_lon: 180.0, max_lon: -180.0 })
10
+ super()
8
11
  @min_lat = opts[:min_lat].to_f
9
12
  @max_lat = opts[:max_lat].to_f
10
13
  @min_lon = opts[:min_lon].to_f
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module GPX
6
+ # Class to parse GeoJSON LineStrings, MultiLineStrings, Points,
7
+ # and MultiPoint geometric objects to GPX format. For the full
8
+ # specification of GeoJSON, see:
9
+ # http://geojson.org/geojson-spec.html
10
+ # Note that GeoJSON coordinates are specified in lon/lat format,
11
+ # instead of the more traditional lat/lon format.
12
+ #
13
+ class GeoJSON
14
+ class << self
15
+ FEATURE = 'Feature'
16
+ LINESTRING = 'LineString'
17
+ MULTILINESTRING = 'MultiLineString'
18
+ POINT = 'Point'
19
+ MULTIPOINT = 'MultiPoint'
20
+
21
+ # Conversion can be initiated by either specifying a file,
22
+ # file name, or by passing in GeoJSON data as a string.
23
+ # Examples:
24
+ # GPX::GeoJSON.convert_to_gpx(geojson_file: 'mygeojsonfile.json')
25
+ # or
26
+ # file = File.new('mygeojsonfile.json', 'r')
27
+ # GPX::GeoJSON.convert_to_gpx(geojson_file: file)
28
+ # or
29
+ # data = JSON.generate(my_geojson_hash)
30
+ # GPX::GeoJSON.convert_to_gpx(geojson_data: data)
31
+ #
32
+ # Returns a GPX::GPX_File object populated with the converted data.
33
+ #
34
+ def convert_to_gpx(opts = {})
35
+ geojson = geojson_data_from(opts)
36
+ gpx_file = GPX::GPXFile.new
37
+ add_tracks_to(gpx_file, geojson)
38
+ add_waypoints_to(gpx_file, geojson)
39
+ gpx_file
40
+ end
41
+
42
+ private
43
+
44
+ def geojson_data_from(opts)
45
+ if opts[:geojson_file]
46
+ parse_geojson_data_from_file(opts[:geojson_file])
47
+ elsif opts[:geojson_data]
48
+ parse_geojson_data(opts[:geojson_data])
49
+ else
50
+ raise ArgumentError,
51
+ 'Must pass value for \':geojson_file\' ' \
52
+ 'or \':geojson_data\' to convert_to_gpx'
53
+ end
54
+ end
55
+
56
+ def parse_geojson_data_from_file(filename)
57
+ parse_geojson_data(IO.read(filename))
58
+ end
59
+
60
+ def parse_geojson_data(data)
61
+ JSON.parse(data)
62
+ end
63
+
64
+ def add_tracks_to(gpx_file, geojson)
65
+ tracks = [line_strings_to_track(geojson)] +
66
+ multi_line_strings_to_tracks(geojson)
67
+ tracks.compact!
68
+ gpx_file.tracks += tracks
69
+ gpx_file.tracks.each { |t| gpx_file.update_meta_data(t) }
70
+ end
71
+
72
+ def add_waypoints_to(gpx_file, geojson)
73
+ gpx_file.waypoints +=
74
+ points_to_waypoints(geojson, gpx_file) +
75
+ multi_points_to_waypoints(geojson, gpx_file)
76
+ end
77
+
78
+ # Converts GeoJSON 'LineString' features.
79
+ # Current strategy is to convert each LineString into a
80
+ # Track Segment, returning a Track for all LineStrings.
81
+ #
82
+ def line_strings_to_track(geojson)
83
+ line_strings = line_strings_in(geojson)
84
+ return nil unless line_strings.any?
85
+
86
+ track = GPX::Track.new
87
+ line_strings.each do |ls|
88
+ coords = ls['geometry']['coordinates']
89
+ track.append_segment(coords_to_segment(coords))
90
+ end
91
+ track
92
+ end
93
+
94
+ # Converts GeoJSON 'MultiLineString' features.
95
+ # Current strategy is to convert each MultiLineString
96
+ # into a Track, with each set of LineString coordinates
97
+ # within a MultiLineString a Track Segment.
98
+ #
99
+ def multi_line_strings_to_tracks(geojson)
100
+ tracks = []
101
+ multi_line_strings_in(geojson).each do |mls|
102
+ track = GPX::Track.new
103
+ mls['geometry']['coordinates'].each do |coords|
104
+ seg = coords_to_segment(coords)
105
+ seg.track = track
106
+ track.append_segment(seg)
107
+ end
108
+ tracks << track
109
+ end
110
+ tracks
111
+ end
112
+
113
+ # Converts GeoJSON 'Point' features.
114
+ # Current strategy is to convert each Point
115
+ # feature into a GPX waypoint.
116
+ #
117
+ def points_to_waypoints(geojson, gpx_file)
118
+ points_in(geojson).reduce([]) do |acc, pt|
119
+ coords = pt['geometry']['coordinates']
120
+ acc << point_to_waypoint(coords, gpx_file)
121
+ end
122
+ end
123
+
124
+ # Converts GeoJSON 'MultiPoint' features.
125
+ # Current strategy is to convert each coordinate
126
+ # point in a MultiPoint to a GPX waypoint.
127
+ #
128
+ # NOTE: It is debatable that a MultiPoint feature
129
+ # might translate best into a GPX route, which is
130
+ # described as
131
+ # "an ordered list of waypoints representing a
132
+ # series of turn points leading to a destination."
133
+ # See http://www.topografix.com/gpx/1/1/#type_rteType
134
+ #
135
+ def multi_points_to_waypoints(geojson, gpx_file)
136
+ multi_points_in(geojson).reduce([]) do |acc, mpt|
137
+ mpt['geometry']['coordinates'].each do |coords|
138
+ acc << point_to_waypoint(coords, gpx_file)
139
+ end
140
+ end
141
+ end
142
+
143
+ # Given an array of [lng, lat, ele] coordinates,
144
+ # return a GPX track segment.
145
+ #
146
+ def coords_to_segment(coords)
147
+ seg = GPX::Segment.new
148
+ coords.each do |pt|
149
+ seg.append_point(point_to_track_point(pt, seg))
150
+ end
151
+ seg
152
+ end
153
+
154
+ # Given a GeoJSON coordinate point, return
155
+ # a GPX::Waypoint
156
+ def point_to_waypoint(point, gpx_file)
157
+ GPX::Waypoint.new(gpx_file: gpx_file,
158
+ lon: point[0],
159
+ lat: point[1],
160
+ elevation: point[2])
161
+ end
162
+
163
+ # Given a GeoJSON coorindate point, and
164
+ # GPX segment, return a GPX::TrackPoint.
165
+ #
166
+ def point_to_track_point(point, seg)
167
+ GPX::TrackPoint.new(segment: seg,
168
+ lon: point[0],
169
+ lat: point[1],
170
+ elevation: point[2])
171
+ end
172
+
173
+ # Returns all features in the passed geojson
174
+ # that match the type.
175
+ #
176
+ def features_for(geojson, type)
177
+ geojson['features'].find_all do |f|
178
+ f['type'] == FEATURE && f['geometry']['type'] == type
179
+ end
180
+ end
181
+
182
+ def points_in(geojson)
183
+ features_for(geojson, POINT)
184
+ end
185
+
186
+ def multi_points_in(geojson)
187
+ features_for(geojson, MULTIPOINT)
188
+ end
189
+
190
+ def line_strings_in(geojson)
191
+ features_for(geojson, LINESTRING)
192
+ end
193
+
194
+ def multi_line_strings_in(geojson)
195
+ features_for(geojson, MULTILINESTRING)
196
+ end
197
+ end
198
+ end
199
+ end
data/lib/gpx/gpx.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GPX
2
4
  # A common base class which provides a useful initializer method to many
3
5
  # class in the GPX library.
data/lib/gpx/gpx_file.rb CHANGED
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GPX
2
4
  class GPXFile < Base
3
5
  attr_accessor :tracks,
4
6
  :routes, :waypoints, :bounds, :lowest_point, :highest_point, :duration, :ns, :time, :name, :version, :creator, :description, :moving_duration
5
7
 
6
- DEFAULT_CREATOR = "GPX RubyGem #{GPX::VERSION} -- http://dougfales.github.io/gpx/".freeze
8
+ DEFAULT_CREATOR = "GPX RubyGem #{GPX::VERSION} -- http://dougfales.github.io/gpx/"
7
9
 
8
10
  # This initializer can be used to create a new GPXFile from an existing
9
11
  # file or to create a new GPXFile instance with no data (so that you can
@@ -23,6 +25,7 @@ module GPX
23
25
  # gpx_file = GPXFile.new(:tracks => [some_track])
24
26
  #
25
27
  def initialize(opts = {})
28
+ super()
26
29
  @duration = 0
27
30
  @attributes = {}
28
31
  @namespace_defs = []
@@ -43,7 +46,8 @@ module GPX
43
46
  @namespace_defs = gpx_element.namespace_definitions
44
47
  @version = gpx_element['version']
45
48
  reset_meta_data
46
- bounds_element = (begin
49
+ bounds_element = (
50
+ begin
47
51
  @xml.at('metadata/bounds')
48
52
  rescue StandardError
49
53
  nil
@@ -58,20 +62,20 @@ module GPX
58
62
  end
59
63
 
60
64
  @time = begin
61
- Time.parse(@xml.at('metadata/time').inner_text)
62
- rescue StandardError
63
- nil
64
- end
65
+ Time.parse(@xml.at('metadata/time').inner_text)
66
+ rescue StandardError
67
+ nil
68
+ end
65
69
  @name = begin
66
- @xml.at('metadata/name').inner_text
67
- rescue StandardError
68
- nil
69
- end
70
+ @xml.at('metadata/name').inner_text
71
+ rescue StandardError
72
+ nil
73
+ end
70
74
  @description = begin
71
- @xml.at('metadata/desc').inner_text
72
- rescue StandardError
73
- nil
74
- end
75
+ @xml.at('metadata/desc').inner_text
76
+ rescue StandardError
77
+ nil
78
+ end
75
79
  @xml.search('trk').each do |trk|
76
80
  trk = Track.new(element: trk, gpx_file: self)
77
81
  update_meta_data(trk, get_bounds)
@@ -87,7 +91,7 @@ module GPX
87
91
  else
88
92
  reset_meta_data
89
93
  opts.each { |attr_name, value| instance_variable_set("@#{attr_name}", value) }
90
- unless @tracks.nil? || @tracks.size.zero?
94
+ unless @tracks.nil? || @tracks.empty?
91
95
  @tracks.each { |trk| update_meta_data(trk) }
92
96
  calculate_duration
93
97
  end
@@ -103,7 +107,8 @@ module GPX
103
107
  result = el[name]
104
108
  break unless result.nil?
105
109
  end
106
- (begin
110
+ (
111
+ begin
107
112
  result.to_f
108
113
  rescue StandardError
109
114
  nil
@@ -189,6 +194,8 @@ module GPX
189
194
  @moving_duration = 0.0
190
195
  end
191
196
 
197
+ # rubocop:disable Style/OptionalBooleanParameter
198
+
192
199
  # Updates the meta data for this GPX file. Meta data includes the
193
200
  # bounds, the high and low points, and the distance. This is useful when
194
201
  # you modify the GPX data (i.e. by adding or deleting points) and you
@@ -215,6 +222,7 @@ module GPX
215
222
  doc = generate_xml_doc
216
223
  doc.to_xml
217
224
  end
225
+ # rubocop:enable Style/OptionalBooleanParameter
218
226
 
219
227
  def inspect
220
228
  "<#{self.class.name}:...>"
@@ -234,13 +242,13 @@ module GPX
234
242
  # $stderr.puts @namespace_defs.inspect
235
243
  gpx_header = {}
236
244
  @attributes.each do |k, v|
237
- k = v.namespace.prefix + ':' + k if v.namespace
245
+ k = "#{v.namespace.prefix}:#{k}" if v.namespace
238
246
  gpx_header[k] = v.value
239
247
  end
240
248
 
241
249
  @namespace_defs.each do |nsd|
242
250
  tag = 'xmlns'
243
- tag += ':' + nsd.prefix if nsd.prefix
251
+ tag += ":#{nsd.prefix}" if nsd.prefix
244
252
  gpx_header[tag] = nsd.href
245
253
  end
246
254
  gpx_header
@@ -260,7 +268,7 @@ module GPX
260
268
  # $stderr.puts gpx_header.keys.inspect
261
269
 
262
270
  # rubocop:disable Metrics/BlockLength
263
- doc = Nokogiri::XML::Builder.new do |xml|
271
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
264
272
  xml.gpx(gpx_header) do
265
273
  # version 1.0 of the schema doesn't support the metadata element, so push them straight to the root 'gpx' element
266
274
  if @version == '1.0'
@@ -285,19 +293,17 @@ module GPX
285
293
  end
286
294
  end
287
295
 
288
- unless tracks.nil?
289
- tracks.each do |t|
290
- xml.trk do
291
- xml.name t.name
292
-
293
- t.segments.each do |seg|
294
- xml.trkseg do
295
- seg.points.each do |p|
296
- xml.trkpt(lat: p.lat, lon: p.lon) do
297
- xml.time p.time.xmlschema unless p.time.nil?
298
- xml.ele p.elevation unless p.elevation.nil?
299
- xml << p.extensions.to_xml unless p.extensions.nil?
300
- end
296
+ tracks&.each do |t|
297
+ xml.trk do
298
+ xml.name t.name
299
+
300
+ t.segments.each do |seg|
301
+ xml.trkseg do
302
+ seg.points.each do |p|
303
+ xml.trkpt(lat: p.lat, lon: p.lon) do
304
+ xml.time p.time.xmlschema unless p.time.nil?
305
+ xml.ele p.elevation unless p.elevation.nil?
306
+ xml << p.extensions.to_xml unless p.extensions.nil?
301
307
  end
302
308
  end
303
309
  end
@@ -305,27 +311,23 @@ module GPX
305
311
  end
306
312
  end
307
313
 
308
- unless waypoints.nil?
309
- waypoints.each do |w|
310
- xml.wpt(lat: w.lat, lon: w.lon) do
311
- xml.time w.time.xmlschema unless w.time.nil?
312
- Waypoint::SUB_ELEMENTS.each do |sub_elem|
313
- xml.send(sub_elem, w.send(sub_elem)) if w.respond_to?(sub_elem) && !w.send(sub_elem).nil?
314
- end
314
+ waypoints&.each do |w|
315
+ xml.wpt(lat: w.lat, lon: w.lon) do
316
+ xml.time w.time.xmlschema unless w.time.nil?
317
+ Waypoint::SUB_ELEMENTS.each do |sub_elem|
318
+ xml.send(sub_elem, w.send(sub_elem)) if w.respond_to?(sub_elem) && !w.send(sub_elem).nil?
315
319
  end
316
320
  end
317
321
  end
318
322
 
319
- unless routes.nil?
320
- routes.each do |r|
321
- xml.rte do
322
- xml.name r.name
323
+ routes&.each do |r|
324
+ xml.rte do
325
+ xml.name r.name
323
326
 
324
- r.points.each do |p|
325
- xml.rtept(lat: p.lat, lon: p.lon) do
326
- xml.time p.time.xmlschema unless p.time.nil?
327
- xml.ele p.elevation unless p.elevation.nil?
328
- end
327
+ r.points.each do |p|
328
+ xml.rtept(lat: p.lat, lon: p.lon) do
329
+ xml.time p.time.xmlschema unless p.time.nil?
330
+ xml.ele p.elevation unless p.elevation.nil?
329
331
  end
330
332
  end
331
333
  end
@@ -333,17 +335,17 @@ module GPX
333
335
  end
334
336
  end
335
337
  # rubocop:enable Metrics/BlockLength
336
-
337
- doc
338
338
  end
339
339
 
340
340
  # Calculates and sets the duration attribute by subtracting the time on
341
341
  # the very first point from the time on the very last point.
342
342
  def calculate_duration
343
343
  @duration = 0
344
- if @tracks.nil? || @tracks.size.zero? || @tracks[0].segments.nil? || @tracks[0].segments.size.zero?
344
+ if @tracks.nil? || @tracks.empty? || @tracks[0].segments.nil? || @tracks[0].segments.empty?
345
345
  return @duration
346
+
346
347
  end
348
+
347
349
  @duration = (@tracks[-1].segments[-1].points[-1].time - @tracks.first.segments.first.points.first.time)
348
350
  rescue StandardError
349
351
  @duration = 0