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.
- 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
|