gpx_doctor 0.5.0 → 0.6.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/README.md +8 -3
- data/lib/gpx_doctor/models/waypoint.rb +1 -1
- data/lib/gpx_doctor/parser.rb +10 -0
- data/lib/gpx_doctor/point_labeler.rb +96 -0
- data/lib/gpx_doctor/version.rb +1 -1
- data/lib/gpx_doctor.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 62f11c008dcbf53c4585d1cc1d4935555ddecbfc98ce4db4f0141476f2847ce3
|
|
4
|
+
data.tar.gz: ebf37bed3a22db9e84a6a0d1b593f2e3f9e9bf0146f114a06d8b900cc2e853f8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b096d0f32832c3ecd0147bd5b6066dd18f1fd19233af68101d486b5a4895febb2228da30fd907b849436a1feaca98bc707cfd2a0963b05b0587962d11a803f85
|
|
7
|
+
data.tar.gz: 5cf20d498a2247483904a86ca637bff0cc527bafe74f7c312d869694ef05fa798f675940f536980240eab12a90034bed42e841bcc6056bf43fb5505484baddc8
|
data/README.md
CHANGED
|
@@ -89,6 +89,7 @@ result = GpxDoctor::Parser.parse("path/to/file.gpx", params: {
|
|
|
89
89
|
max_points: 500, # reduce each segment to at most this many points
|
|
90
90
|
segment_statistics: true, # compute distance_to_next, elevation_change, direction for each point
|
|
91
91
|
cumulative_distance: true, # add cumulative distance from start of each segment/route
|
|
92
|
+
label_interval: 1.0, # insert an interpolated, labelled point at every interval mark (kilometres or miles based on unit_system)
|
|
92
93
|
enhance_elevation: true, # fetch missing elevations from the configured elevation server
|
|
93
94
|
full_poi_data: true # populate result.pois with start/finish boundary data for the whole GPX and each track segment
|
|
94
95
|
})
|
|
@@ -100,13 +101,16 @@ Processing is applied in the following order:
|
|
|
100
101
|
2. `max_points` — point reduction
|
|
101
102
|
3. `segment_statistics` — per-point statistics (distance, bearing, elevation change)
|
|
102
103
|
4. `cumulative_distance` — cumulative distance from the start of each segment/route
|
|
103
|
-
5. `
|
|
104
|
-
6. `
|
|
104
|
+
5. `label_interval` — labelled point insertion
|
|
105
|
+
6. `enhance_elevation` — elevation lookup via the elevation server
|
|
106
|
+
7. `full_poi_data` — POI boundary extraction (start, finish, and per-segment boundaries)
|
|
105
107
|
|
|
106
108
|
`enhance_elevation: true` requires the elevation server to be configured (see **Configuration** above). It only fills in points that have no elevation value; existing elevations are left unchanged.
|
|
107
109
|
|
|
108
110
|
`cumulative_distance: true` adds a `cumulative_distance` field to each point, representing the cumulative distance in kilometers from the start of its segment or route. For tracks with multiple segments, each segment's cumulative distance starts at 0.0 (gaps between segments are not included in the calculation).
|
|
109
111
|
|
|
112
|
+
`label_interval: 1.0` walks each route/segment and inserts an interpolated point at every multiple of the given interval (measured as cumulative distance from the start, in kilometres for `:metric` or miles for `:imperial` — see **Unit System** above), setting the new point's `label` field to that distance (e.g. `1.0` for the point at the 1.0 km/mi mark). A mark falling on an existing point (within a small tolerance) is skipped rather than duplicated. `label_interval` runs after `cumulative_distance`, reusing its `cumulative_distance` values instead of recalculating them when `cumulative_distance: true` is also given. **Because `label_interval` is applied after `max_points`, the resulting number of points may exceed `max_points`.**
|
|
113
|
+
|
|
110
114
|
`full_poi_data: true` populates `result.pois` with the first and last geographic point of the entire GPX (across all routes and track segments), plus optional per-segment boundary data. Distances within each segment start at 0.0 and reflect that segment's length only. The `ele` key is omitted for points that have no elevation value. The global `finish` distance is the sum of all individual collection lengths. See **`result.pois`** below for the output shape.
|
|
111
115
|
|
|
112
116
|
## Accessing data
|
|
@@ -294,7 +298,8 @@ GpxDoctor::Builder.build_file(result, 'output.gpx')
|
|
|
294
298
|
| `distance_to_next` | Float | Distance to next point (metres or feet based on `unit_system`) — set by `segment_statistics: true` |
|
|
295
299
|
| `elevation_change` | Float | Elevation change to next point (metres or feet based on `unit_system`) — set by `segment_statistics: true` |
|
|
296
300
|
| `direction` | Float | Bearing to next point (0–360°) — set by `segment_statistics: true` |
|
|
297
|
-
| `cumulative_distance` | Float | Cumulative distance from segment/route start (kilometres or miles based on `unit_system`) — set by `cumulative_distance: true` |
|
|
301
|
+
| `cumulative_distance` | Float | Cumulative distance from segment/route start (kilometres or miles based on `unit_system`) — set by `cumulative_distance: true`; also set (to the same value as `label`) for points inserted by `label_interval` |
|
|
302
|
+
| `label` | Float | Cumulative distance mark for a point inserted by `label_interval` (kilometres or miles based on `unit_system`); `nil` for points not created for labelling — set by `label_interval` |
|
|
298
303
|
|
|
299
304
|
`Waypoint#to_h` returns a hash of all non-nil fields.
|
|
300
305
|
|
|
@@ -8,7 +8,7 @@ module GpxDoctor
|
|
|
8
8
|
sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid
|
|
9
9
|
].freeze
|
|
10
10
|
|
|
11
|
-
STATISTICS_FIELDS = %i[distance_to_next elevation_change direction cumulative_distance].freeze
|
|
11
|
+
STATISTICS_FIELDS = %i[distance_to_next elevation_change direction cumulative_distance label].freeze
|
|
12
12
|
|
|
13
13
|
attr_accessor(*STATISTICS_FIELDS)
|
|
14
14
|
|
data/lib/gpx_doctor/parser.rb
CHANGED
|
@@ -54,6 +54,7 @@ module GpxDoctor
|
|
|
54
54
|
select_max_points(result) if @params[:max_points]
|
|
55
55
|
enhance_statistics(result) if @params[:segment_statistics]
|
|
56
56
|
enhance_cumulative_distance(result) if @params[:cumulative_distance]
|
|
57
|
+
label_points(result) if @params[:label_interval]
|
|
57
58
|
enhance_elevations(result) if @params[:enhance_elevation]
|
|
58
59
|
result.pois = build_pois(result) if @params[:full_poi_data]
|
|
59
60
|
|
|
@@ -278,6 +279,15 @@ module GpxDoctor
|
|
|
278
279
|
end
|
|
279
280
|
end
|
|
280
281
|
|
|
282
|
+
def label_points(result)
|
|
283
|
+
labeler = PointLabeler.new
|
|
284
|
+
interval = @params[:label_interval]
|
|
285
|
+
result.routes.each { |route| route.points = labeler.label(route.points, interval) }
|
|
286
|
+
result.tracks.each do |track|
|
|
287
|
+
track.segments.each { |seg| seg.points = labeler.label(seg.points, interval) }
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
281
291
|
def point_to_poi(point, distance)
|
|
282
292
|
poi = { lon: point.lon, lat: point.lat }
|
|
283
293
|
poi[:ele] = point.ele if point.ele
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GpxDoctor
|
|
4
|
+
class PointLabeler
|
|
5
|
+
# Tolerance (in the configured distance unit) used to decide whether a
|
|
6
|
+
# label distance coincides with an existing point, to avoid inserting a
|
|
7
|
+
# near-duplicate point right next to it.
|
|
8
|
+
EPSILON = 1e-9
|
|
9
|
+
|
|
10
|
+
# Inserts an interpolated point at every multiple of +label_interval+
|
|
11
|
+
# measured as cumulative distance from the start of +points+ (kilometres
|
|
12
|
+
# for :metric, miles for :imperial — see GpxDoctor.configuration.unit_system).
|
|
13
|
+
#
|
|
14
|
+
# When +points+ already carry a +cumulative_distance+ (set by
|
|
15
|
+
# GpxDoctor::CumulativeDistanceEnhancer), those values are reused instead
|
|
16
|
+
# of being recalculated. Otherwise cumulative distance is computed from
|
|
17
|
+
# scratch.
|
|
18
|
+
#
|
|
19
|
+
# Existing points are left untouched. A new point is inserted between the
|
|
20
|
+
# two existing points that bracket each label distance, with lat/lon
|
|
21
|
+
# always interpolated, and ele/time interpolated only when both endpoints
|
|
22
|
+
# have values. The new point's +label+ field is set to the target
|
|
23
|
+
# distance (e.g. 1.0 for the point at the 1.0 km/mi mark).
|
|
24
|
+
#
|
|
25
|
+
# A label distance that falls (within a small tolerance) on an existing
|
|
26
|
+
# point is skipped — no duplicate point is inserted.
|
|
27
|
+
#
|
|
28
|
+
# Returns a new array; the original is not mutated. When +points+ has
|
|
29
|
+
# fewer than 2 elements, or +label_interval+ is nil, zero or negative,
|
|
30
|
+
# the original array is returned unchanged.
|
|
31
|
+
def label(points, label_interval)
|
|
32
|
+
return points if points.nil? || points.size < 2 || label_interval.nil? || label_interval <= 0
|
|
33
|
+
|
|
34
|
+
cumulative = cumulative_distances(points)
|
|
35
|
+
|
|
36
|
+
result = [points.first]
|
|
37
|
+
label_interval = label_interval.to_f
|
|
38
|
+
target = label_interval
|
|
39
|
+
|
|
40
|
+
points.each_cons(2).with_index do |(a, b), i|
|
|
41
|
+
start_dist = cumulative[i]
|
|
42
|
+
end_dist = cumulative[i + 1]
|
|
43
|
+
|
|
44
|
+
while target <= end_dist + EPSILON
|
|
45
|
+
if target > start_dist + EPSILON && target < end_dist - EPSILON
|
|
46
|
+
result << interpolate(a, b, start_dist, end_dist, target)
|
|
47
|
+
end
|
|
48
|
+
target += label_interval
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
result << b
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
result
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
# Returns the cumulative distance of every point, reusing each point's
|
|
60
|
+
# already-computed +cumulative_distance+ (see CumulativeDistanceEnhancer)
|
|
61
|
+
# when available, to avoid walking the points and recalculating distances
|
|
62
|
+
# a second time.
|
|
63
|
+
def cumulative_distances(points)
|
|
64
|
+
return points.map(&:cumulative_distance) if precomputed?(points)
|
|
65
|
+
|
|
66
|
+
unit_system = GpxDoctor.configuration.unit_system
|
|
67
|
+
|
|
68
|
+
cumulative = [0.0]
|
|
69
|
+
points.each_cons(2) do |a, b|
|
|
70
|
+
distance_km = DistanceCalculator.distance(a, b) / 1000.0
|
|
71
|
+
distance_converted = UnitConverter.convert_cumulative_distance(distance_km, unit_system)
|
|
72
|
+
cumulative << cumulative.last + distance_converted
|
|
73
|
+
end
|
|
74
|
+
cumulative
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def precomputed?(points)
|
|
78
|
+
points.all? { |p| p.respond_to?(:cumulative_distance) && !p.cumulative_distance.nil? }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def interpolate(a, b, start_dist, end_dist, target)
|
|
82
|
+
fraction = (target - start_dist) / (end_dist - start_dist)
|
|
83
|
+
ele = a.ele && b.ele ? a.ele + fraction * (b.ele - a.ele) : nil
|
|
84
|
+
time = a.time && b.time ? a.time + fraction * (b.time - a.time) : nil
|
|
85
|
+
|
|
86
|
+
Models::Waypoint.new(
|
|
87
|
+
lat: a.lat + fraction * (b.lat - a.lat),
|
|
88
|
+
lon: a.lon + fraction * (b.lon - a.lon),
|
|
89
|
+
ele: ele,
|
|
90
|
+
time: time,
|
|
91
|
+
label: target,
|
|
92
|
+
cumulative_distance: target
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/gpx_doctor/version.rb
CHANGED
data/lib/gpx_doctor.rb
CHANGED
|
@@ -21,6 +21,7 @@ require 'gpx_doctor/statistics_enhancer'
|
|
|
21
21
|
require 'gpx_doctor/cumulative_distance_enhancer'
|
|
22
22
|
require 'gpx_doctor/segment_splitter'
|
|
23
23
|
require 'gpx_doctor/point_selector'
|
|
24
|
+
require 'gpx_doctor/point_labeler'
|
|
24
25
|
require 'gpx_doctor/parser'
|
|
25
26
|
require 'gpx_doctor/builder'
|
|
26
27
|
require 'gpx_doctor/geojson_builder'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gpx_doctor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Poltrax
|
|
@@ -63,6 +63,7 @@ files:
|
|
|
63
63
|
- lib/gpx_doctor/models/track_segment.rb
|
|
64
64
|
- lib/gpx_doctor/models/waypoint.rb
|
|
65
65
|
- lib/gpx_doctor/parser.rb
|
|
66
|
+
- lib/gpx_doctor/point_labeler.rb
|
|
66
67
|
- lib/gpx_doctor/point_selector.rb
|
|
67
68
|
- lib/gpx_doctor/segment_splitter.rb
|
|
68
69
|
- lib/gpx_doctor/statistics_enhancer.rb
|