gpx 1.0.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +37 -0
- data/.rubocop +1 -0
- data/.rubocop.yml +2 -5
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/.travis.yml +4 -5
- data/CHANGELOG.md +14 -0
- data/Gemfile +2 -0
- data/README.md +21 -5
- data/Rakefile +7 -0
- data/UPGRADING.md +7 -0
- data/bin/gpx_distance +2 -0
- data/bin/gpx_smooth +5 -2
- data/gpx.gemspec +2 -2
- data/lib/gpx/bounds.rb +3 -0
- data/lib/gpx/geo_json.rb +199 -0
- data/lib/gpx/gpx.rb +2 -0
- data/lib/gpx/gpx_file.rb +53 -51
- data/lib/gpx/magellan_track_log.rb +3 -1
- data/lib/gpx/point.rb +5 -1
- data/lib/gpx/route.rb +4 -1
- data/lib/gpx/segment.rb +14 -9
- data/lib/gpx/track.rb +11 -4
- data/lib/gpx/track_point.rb +2 -0
- data/lib/gpx/version.rb +3 -1
- data/lib/gpx/waypoint.rb +3 -0
- data/lib/gpx.rb +3 -1
- data/tests/geojson_files/combined_data.json +68 -0
- data/tests/geojson_files/line_string_data.json +83 -0
- data/tests/geojson_files/multi_line_string_data.json +74 -0
- data/tests/geojson_files/multi_point_data.json +14 -0
- data/tests/geojson_files/point_data.json +22 -0
- data/tests/geojson_test.rb +92 -0
- data/tests/gpx10_test.rb +2 -0
- data/tests/gpx_file_test.rb +2 -0
- data/tests/gpx_files/one_segment_mixed_times.gpx +884 -0
- data/tests/gpx_files/routes_without_names.gpx +29 -0
- data/tests/magellan_test.rb +2 -0
- data/tests/output_test.rb +3 -1
- data/tests/route_test.rb +52 -0
- data/tests/segment_test.rb +13 -1
- data/tests/track_file_test.rb +3 -0
- data/tests/track_point_test.rb +2 -0
- data/tests/track_test.rb +2 -0
- data/tests/waypoint_test.rb +2 -0
- metadata +25 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b894a8f06a91646472aa6311602be3da1c0d4767a7bc44867ba954ecf93d1909
|
4
|
+
data.tar.gz: 73b5b4e73fb783f3e10a007e2101f49db0e57f48e3675fea7b8255d1a21b84ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
+
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/
|
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
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
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,
|
14
|
-
|
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.
|
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
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
|
-
|
9
|
+
case str
|
10
|
+
when /\A\d{10}\Z/
|
8
11
|
Time.at(str.to_i)
|
9
|
-
|
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 = '
|
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
|
data/lib/gpx/geo_json.rb
ADDED
@@ -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
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/"
|
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 = (
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
+
Time.parse(@xml.at('metadata/time').inner_text)
|
66
|
+
rescue StandardError
|
67
|
+
nil
|
68
|
+
end
|
65
69
|
@name = begin
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
+
@xml.at('metadata/name').inner_text
|
71
|
+
rescue StandardError
|
72
|
+
nil
|
73
|
+
end
|
70
74
|
@description = begin
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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.
|
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
|
-
(
|
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
|
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 +=
|
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
|
-
|
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
|
-
|
289
|
-
|
290
|
-
xml.
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
xml.
|
297
|
-
|
298
|
-
|
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
|
-
|
309
|
-
|
310
|
-
xml.
|
311
|
-
|
312
|
-
|
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
|
-
|
320
|
-
|
321
|
-
xml.
|
322
|
-
xml.name r.name
|
323
|
+
routes&.each do |r|
|
324
|
+
xml.rte do
|
325
|
+
xml.name r.name
|
323
326
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
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.
|
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
|