gpx 1.0.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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