geodetic 0.3.1 → 0.3.2
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/CHANGELOG.md +27 -0
- data/README.md +43 -1
- data/docs/index.md +3 -1
- data/docs/reference/feature.md +3 -2
- data/docs/reference/path.md +269 -0
- data/examples/06_path_operations.rb +368 -0
- data/examples/README.md +23 -0
- data/lib/geodetic/feature.rb +10 -2
- data/lib/geodetic/path.rb +599 -0
- data/lib/geodetic/version.rb +1 -1
- data/lib/geodetic.rb +1 -0
- data/mkdocs.yml +1 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f295df82e85611a9a7a7dd56593ae029d6ad096cc5c96021d5b59b4f4b001cc2
|
|
4
|
+
data.tar.gz: 6da1bc3461fd4f372b53f5458d322f8c14d1feb8a2afa93a2375cb5641674674
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1a0b72935abf28bbcb41182f800d1bdb52466530565f13d75fec5f3eec14c0dac7680f8220f09c51cf4d60ed3b87fc53a3675ceb972f1cec17ca06cbd096d717
|
|
7
|
+
data.tar.gz: 754e5b64d8e1d333dafd270a6e21434d46102ea57e43fbba16b83bcecd9eb69eb4e834a51554c10eb59433f6d434930bf2664fca0d9c22b37cf7378e90cc106e
|
data/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
## [0.3.2] - 2026-03-09
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`Geodetic::Path` class** — directed, ordered sequence of unique coordinates for modeling routes, trails, and boundaries
|
|
19
|
+
- **Navigation**: `first`, `last`, `next`, `prev`, `segments`, `size`, `empty?`
|
|
20
|
+
- **Membership**: `include?`/`includes?` (waypoint check), `contains?`/`inside?` (on-segment check with configurable tolerance)
|
|
21
|
+
- **Spatial**: `nearest_waypoint`, `closest_coordinate_to`, `distance_to`, `bearing_to` using geometric projection onto segments
|
|
22
|
+
- **Closest points**: `closest_points_to` for Path-to-Path, Path-to-Polygon, Path-to-Rectangle, and Path-to-Circle
|
|
23
|
+
- **Computed**: `total_distance`, `segment_distances`, `segment_bearings`, `reverse`
|
|
24
|
+
- **Subpath/split**: `between(from, to)` extracts a subpath; `split_at(coord)` divides into two paths sharing the split point
|
|
25
|
+
- **Interpolation**: `at_distance(distance)` finds the coordinate at a given distance along the path
|
|
26
|
+
- **Bounding box**: `bounds` returns an `Areas::Rectangle`
|
|
27
|
+
- **Polygon conversion**: `to_polygon` closes the path (validates no self-intersection)
|
|
28
|
+
- **Intersection**: `intersects?(other_path)` detects crossing segments
|
|
29
|
+
- **Equality**: `==` compares coordinates in order
|
|
30
|
+
- **Enumerable**: includes `Enumerable` via `each` — supports `map`, `select`, `any?`, `to_a`, etc.
|
|
31
|
+
- **Non-mutating operators**: `+` and `-` accept both coordinates and paths
|
|
32
|
+
- **Mutating operators**: `<<`, `>>`, `prepend`, `insert(after:/before:)`, `delete`/`remove` — all accept paths as well as coordinates
|
|
33
|
+
- **Path operations example** (`examples/06_path_operations.rb`) — 19-section demo covering all Path capabilities with a Manhattan walking route
|
|
34
|
+
- Documentation: `docs/reference/path.md` (Path reference)
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Updated `Geodetic::Feature` to support Path as a geometry type — delegates `distance_to` and `bearing_to` using geometric projection
|
|
39
|
+
- Updated README, `docs/index.md`, `docs/reference/feature.md`, `examples/README.md`, and mkdocs nav to include Path class
|
|
40
|
+
|
|
14
41
|
## [0.3.1] - 2026-03-09
|
|
15
42
|
|
|
16
43
|
### Added
|
data/README.md
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
- <strong>Bearing Calculations</strong> - Forward azimuth, back azimuth, compass directions, elevation angles<br>
|
|
20
20
|
- <strong>Geoid Height Support</strong> - EGM96, EGM2008, GEOID18, GEOID12B models<br>
|
|
21
21
|
- <strong>Geographic Areas</strong> - Circle, Polygon, and Rectangle with point-in-area tests<br>
|
|
22
|
+
- <strong>Paths</strong> - Directed coordinate sequences with navigation, interpolation, closest approach, intersection, and area conversion<br>
|
|
22
23
|
- <strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
|
|
23
24
|
- <strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
24
25
|
- <strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
|
|
@@ -541,9 +542,49 @@ rect.sw # => computed SW corner
|
|
|
541
542
|
rect.includes?(point) # => true/false
|
|
542
543
|
```
|
|
543
544
|
|
|
545
|
+
### Paths
|
|
546
|
+
|
|
547
|
+
`Path` is a directed, ordered sequence of unique coordinates representing routes, trails, or boundaries.
|
|
548
|
+
|
|
549
|
+
```ruby
|
|
550
|
+
route = Path.new(coordinates: [battery_park, wall_street, brooklyn_bridge, city_hall])
|
|
551
|
+
|
|
552
|
+
# Navigation
|
|
553
|
+
route.first # => starting waypoint
|
|
554
|
+
route.next(wall_street) # => brooklyn_bridge
|
|
555
|
+
route.total_distance.to_km # => "3.42 km"
|
|
556
|
+
|
|
557
|
+
# Build incrementally
|
|
558
|
+
trail = Path.new
|
|
559
|
+
trail << start << middle << finish
|
|
560
|
+
trail >> new_start # prepend
|
|
561
|
+
|
|
562
|
+
# Combine paths
|
|
563
|
+
combined = downtown + uptown # concatenate
|
|
564
|
+
trimmed = combined - detour # remove coordinates
|
|
565
|
+
|
|
566
|
+
# Closest approach (geometric projection, not just waypoints)
|
|
567
|
+
route.closest_coordinate_to(off_path_point)
|
|
568
|
+
route.distance_to(target)
|
|
569
|
+
route.closest_points_to(other_path) # path-to-path
|
|
570
|
+
|
|
571
|
+
# Spatial operations
|
|
572
|
+
sub = route.between(a, b) # extract subpath
|
|
573
|
+
left, right = route.split_at(c) # split at waypoint
|
|
574
|
+
route.at_distance(Distance.km(2)) # interpolate along path
|
|
575
|
+
route.bounds # => Areas::Rectangle
|
|
576
|
+
route.to_polygon # close into polygon
|
|
577
|
+
route.intersects?(other_path) # crossing detection
|
|
578
|
+
route.contains?(point) # on-segment check
|
|
579
|
+
|
|
580
|
+
# Enumerable
|
|
581
|
+
route.map { |c| c.lat }
|
|
582
|
+
route.select { |c| c.lat > 40.72 }
|
|
583
|
+
```
|
|
584
|
+
|
|
544
585
|
### Features
|
|
545
586
|
|
|
546
|
-
`Feature` wraps a geometry (any coordinate or
|
|
587
|
+
`Feature` wraps a geometry (any coordinate, area, or path) with a label and a metadata hash. It delegates `distance_to` and `bearing_to` to its geometry, using the centroid for area geometries.
|
|
547
588
|
|
|
548
589
|
```ruby
|
|
549
590
|
liberty = Feature.new(
|
|
@@ -600,6 +641,7 @@ The [`examples/`](examples/) directory contains runnable demo scripts showing pr
|
|
|
600
641
|
| [`03_distance_calculations.rb`](examples/03_distance_calculations.rb) | Distance class features, unit conversions, and arithmetic |
|
|
601
642
|
| [`04_bearing_calculations.rb`](examples/04_bearing_calculations.rb) | Bearing class, compass directions, elevation angles, and chain bearings |
|
|
602
643
|
| [`05_map_rendering/`](examples/05_map_rendering/) | Render landmarks on a raster map with Feature objects, polygon areas, bearing arrows, and icons using [libgd-gis](https://rubygems.org/gems/libgd-gis) |
|
|
644
|
+
| [`06_path_operations.rb`](examples/06_path_operations.rb) | Path class: construction, navigation, mutation, path arithmetic, closest approach, containment, Enumerable, equality, subpaths, split, interpolation, bounding boxes, polygon conversion, intersection, path-to-path/area closest points, and Feature integration |
|
|
603
645
|
|
|
604
646
|
Run any example with:
|
|
605
647
|
|
data/docs/index.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<li><strong>Bearing Calculations</strong> - Forward azimuth, back azimuth, compass directions, elevation angles<br>
|
|
16
16
|
<li><strong>Geoid Height Support</strong> - EGM96, EGM2008, GEOID18, GEOID12B models<br>
|
|
17
17
|
<li><strong>Geographic Areas</strong> - Circle, Polygon, and Rectangle with point-in-area tests<br>
|
|
18
|
+
<li><strong>Paths</strong> - Directed coordinate sequences with navigation, interpolation, closest approach, intersection, and area conversion<br>
|
|
18
19
|
<li><strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
|
|
19
20
|
<li><strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
20
21
|
<li><strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
|
|
@@ -57,7 +58,8 @@ Geodetic supports full bidirectional conversion between all 18 coordinate system
|
|
|
57
58
|
- **16 geodetic datums** -- WGS84, GRS 1980, Clarke 1866, Airy 1830, Bessel 1841, and more. All conversion methods accept an optional datum parameter, defaulting to WGS84.
|
|
58
59
|
- **Geoid height calculations** -- Convert between ellipsoidal and orthometric heights using models such as EGM96, EGM2008, GEOID18, and GEOID12B.
|
|
59
60
|
- **Geographic areas** -- `Geodetic::Areas::Circle`, `Geodetic::Areas::Polygon`, and `Geodetic::Areas::Rectangle` for point-in-area testing.
|
|
60
|
-
- **
|
|
61
|
+
- **Paths** -- `Geodetic::Path` is a directed, ordered sequence of unique coordinates supporting navigation, segment analysis, interpolation, closest approach (geometric projection), containment testing, bounding boxes, polygon conversion, and path intersection detection.
|
|
62
|
+
- **Features** -- `Geodetic::Feature` wraps any coordinate, area, or path with a label and metadata hash, delegating `distance_to` and `bearing_to` to the underlying geometry.
|
|
61
63
|
|
|
62
64
|
## Design Principles
|
|
63
65
|
|
data/docs/reference/feature.md
CHANGED
|
@@ -14,7 +14,7 @@ Feature.new(
|
|
|
14
14
|
)
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
The `geometry` parameter accepts any coordinate class
|
|
17
|
+
The `geometry` parameter accepts any coordinate class, any area class (`Circle`, `Polygon`, `Rectangle`), or a `Path`. The `metadata` hash is optional and defaults to `{}`.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -36,8 +36,9 @@ A Feature's geometry can be any of:
|
|
|
36
36
|
|
|
37
37
|
- **Coordinate** — any of the 18 coordinate classes (`LLA`, `ECEF`, `UTM`, etc.)
|
|
38
38
|
- **Area** — `Areas::Circle`, `Areas::Polygon`, or `Areas::Rectangle`
|
|
39
|
+
- **Path** — a `Geodetic::Path` representing a route or trail
|
|
39
40
|
|
|
40
|
-
When the geometry is an area, `distance_to` and `bearing_to` use the area's `centroid` as the reference point.
|
|
41
|
+
When the geometry is an area, `distance_to` and `bearing_to` use the area's `centroid` as the reference point. When the geometry is a Path, `distance_to` and `bearing_to` use geometric projection to find the closest approach point on the path.
|
|
41
42
|
|
|
42
43
|
---
|
|
43
44
|
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Path Reference
|
|
2
|
+
|
|
3
|
+
`Geodetic::Path` represents a directed, ordered sequence of unique coordinates. It models routes, trails, boundaries, and any linear geographic feature where the order of waypoints matters.
|
|
4
|
+
|
|
5
|
+
A Path has a start (first coordinate) and an end (last coordinate). No duplicate coordinates are allowed — each waypoint appears exactly once, enabling unambiguous navigation with `next` and `prev`.
|
|
6
|
+
|
|
7
|
+
Path includes Ruby's `Enumerable` module, so all standard iteration methods (`map`, `select`, `any?`, `to_a`, etc.) are available.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Constructor
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Empty path
|
|
15
|
+
path = Path.new
|
|
16
|
+
|
|
17
|
+
# From an array of coordinates
|
|
18
|
+
path = Path.new(coordinates: [a, b, c, d])
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Raises `ArgumentError` if any coordinate appears more than once.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Attributes
|
|
26
|
+
|
|
27
|
+
| Attribute | Type | Description |
|
|
28
|
+
|---------------|-------|-------------|
|
|
29
|
+
| `coordinates` | Array | The ordered list of waypoints (read-only) |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Navigation
|
|
34
|
+
|
|
35
|
+
| Method | Returns | Description |
|
|
36
|
+
|---------------------|-------------|-------------|
|
|
37
|
+
| `first` | Coordinate | Starting waypoint |
|
|
38
|
+
| `last` | Coordinate | Ending waypoint |
|
|
39
|
+
| `next(coordinate)` | Coordinate | Waypoint after the given one, or `nil` at end |
|
|
40
|
+
| `prev(coordinate)` | Waypoint before the given one, or `nil` at start |
|
|
41
|
+
| `size` | Integer | Number of waypoints |
|
|
42
|
+
| `empty?` | Boolean | True if the path has no waypoints |
|
|
43
|
+
| `segments` | Array | Pairs of consecutive coordinates `[[a,b], [b,c], ...]` |
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Membership
|
|
48
|
+
|
|
49
|
+
| Method | Description |
|
|
50
|
+
|--------|-------------|
|
|
51
|
+
| `include?(coord)` | True if the coordinate is a waypoint in the path |
|
|
52
|
+
| `includes?(coord)` | Alias for `include?` |
|
|
53
|
+
| `contains?(coord, tolerance: 10.0)` | True if the coordinate lies on any segment within tolerance (meters) |
|
|
54
|
+
| `inside?(coord, tolerance: 10.0)` | Alias for `contains?` |
|
|
55
|
+
| `excludes?(coord, tolerance: 10.0)` | Opposite of `contains?` |
|
|
56
|
+
| `exclude?(coord)` | Alias for `excludes?` |
|
|
57
|
+
| `outside?(coord)` | Alias for `excludes?` |
|
|
58
|
+
|
|
59
|
+
`includes?` checks waypoints only. `contains?` checks whether a coordinate lies on the line between any two consecutive waypoints, using a bearing comparison with a tolerance derived from the segment length.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Equality
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
path1 == path2
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Two paths are equal if they have the same coordinates in the same order.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Spatial Methods
|
|
74
|
+
|
|
75
|
+
| Method | Returns | Description |
|
|
76
|
+
|--------|---------|-------------|
|
|
77
|
+
| `nearest_waypoint(target)` | Coordinate | The waypoint closest to the target |
|
|
78
|
+
| `closest_coordinate_to(target)` | Coordinate | The closest point on the path (projected onto segments) |
|
|
79
|
+
| `distance_to(other)` | Distance | Distance from the closest point on the path to the target |
|
|
80
|
+
| `bearing_to(other)` | Bearing | Bearing from the closest point on the path to the target |
|
|
81
|
+
| `closest_points_to(other)` | Hash | Closest pair between path and an Area or another Path |
|
|
82
|
+
|
|
83
|
+
The `other` parameter for `distance_to`, `bearing_to`, and `closest_coordinate_to` can be a coordinate, a Feature, an Area, or another Path.
|
|
84
|
+
|
|
85
|
+
### Closest Points
|
|
86
|
+
|
|
87
|
+
`closest_points_to` returns a hash with:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
{
|
|
91
|
+
path_point: Coordinate, # closest point on this path
|
|
92
|
+
area_point: Coordinate, # closest point on the other geometry
|
|
93
|
+
distance: Distance # distance between the two points
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Accepts `Areas::Circle`, `Areas::Polygon`, `Areas::Rectangle`, or another `Path`.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Computed Properties
|
|
102
|
+
|
|
103
|
+
| Method | Returns | Description |
|
|
104
|
+
|--------|---------|-------------|
|
|
105
|
+
| `total_distance` | Distance | Sum of all segment distances |
|
|
106
|
+
| `segment_distances` | Array | Distance for each segment |
|
|
107
|
+
| `segment_bearings` | Array | Bearing for each segment |
|
|
108
|
+
| `reverse` | Path | New path with coordinates in reverse order |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Subpath and Split
|
|
113
|
+
|
|
114
|
+
### `between(from, to)`
|
|
115
|
+
|
|
116
|
+
Extracts a subpath between two waypoints (inclusive). Both must exist in the path, and `from` must precede `to`.
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
sub = route.between(wall_street, union_square)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `split_at(coordinate)`
|
|
123
|
+
|
|
124
|
+
Splits the path at a waypoint, returning two paths that share the split point.
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
left, right = route.split_at(city_hall)
|
|
128
|
+
# left ends with city_hall, right starts with city_hall
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Interpolation
|
|
134
|
+
|
|
135
|
+
### `at_distance(distance)`
|
|
136
|
+
|
|
137
|
+
Returns the coordinate at a given distance along the path from the start. Accepts a `Distance` object or a numeric value in meters.
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
halfway = route.at_distance(route.total_distance.meters / 2.0)
|
|
141
|
+
quarter = route.at_distance(Distance.new(route.total_distance.meters * 0.25))
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Returns the last coordinate if the distance exceeds the total path length.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Bounding Box
|
|
149
|
+
|
|
150
|
+
### `bounds`
|
|
151
|
+
|
|
152
|
+
Returns an `Areas::Rectangle` representing the axis-aligned bounding box of all waypoints.
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
bbox = route.bounds
|
|
156
|
+
bbox.nw # => northwest corner
|
|
157
|
+
bbox.se # => southeast corner
|
|
158
|
+
bbox.includes?(some_point) # => true/false
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## To Polygon
|
|
164
|
+
|
|
165
|
+
### `to_polygon`
|
|
166
|
+
|
|
167
|
+
Closes the path into an `Areas::Polygon` by connecting the last coordinate to the first. Requires at least 3 coordinates. Raises `ArgumentError` if the closing segment would intersect any interior segment of the path.
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
triangle = Path.new(coordinates: [a, b, c])
|
|
171
|
+
poly = triangle.to_polygon
|
|
172
|
+
poly.includes?(some_point)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Intersection
|
|
178
|
+
|
|
179
|
+
### `intersects?(other_path)`
|
|
180
|
+
|
|
181
|
+
Returns true if any segment of this path crosses any segment of the other path. Uses orientation-based intersection testing.
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
route.intersects?(crosstown) # => true/false
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Non-Mutating Operators
|
|
190
|
+
|
|
191
|
+
These return new Path objects; the original is unchanged.
|
|
192
|
+
|
|
193
|
+
| Operator | Accepts | Description |
|
|
194
|
+
|----------|---------|-------------|
|
|
195
|
+
| `+ coordinate` | Coordinate | New path with coordinate appended |
|
|
196
|
+
| `+ path` | Path | New path with all coordinates of the other path appended |
|
|
197
|
+
| `- coordinate` | Coordinate | New path with coordinate removed |
|
|
198
|
+
| `- path` | Path | New path with all of the other path's coordinates removed |
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
combined = downtown_path + uptown_path
|
|
202
|
+
trimmed = full_route - detour_path
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Raises `ArgumentError` if `+` would create duplicates, or if `-` references coordinates not in the path.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Mutating Operators
|
|
210
|
+
|
|
211
|
+
These modify the path in place and return `self` for chaining.
|
|
212
|
+
|
|
213
|
+
| Method | Accepts | Description |
|
|
214
|
+
|--------|---------|-------------|
|
|
215
|
+
| `<< other` | Coordinate or Path | Append to end |
|
|
216
|
+
| `>> other` | Coordinate or Path | Prepend to start |
|
|
217
|
+
| `prepend(other)` | Coordinate or Path | Same as `>>` |
|
|
218
|
+
| `insert(coord, after: ref)` | Coordinate | Insert after a reference waypoint |
|
|
219
|
+
| `insert(coord, before: ref)` | Coordinate | Insert before a reference waypoint |
|
|
220
|
+
| `delete(coord)` | Coordinate | Remove a waypoint |
|
|
221
|
+
| `remove(coord)` | Coordinate | Alias for `delete` |
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
path = Path.new
|
|
225
|
+
path << a << b << c # build incrementally
|
|
226
|
+
path >> start_point # prepend
|
|
227
|
+
path << other_path # append an entire path
|
|
228
|
+
path.insert(detour, after: b) # insert between waypoints
|
|
229
|
+
path.delete(b) # remove a waypoint
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Enumerable
|
|
235
|
+
|
|
236
|
+
Path includes `Enumerable`. The `each` method iterates over coordinates in order.
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
route.map { |c| c.lat }
|
|
240
|
+
route.select { |c| c.lat > 40.72 }
|
|
241
|
+
route.max_by { |c| c.lat }
|
|
242
|
+
route.to_a
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Display
|
|
248
|
+
|
|
249
|
+
| Method | Returns |
|
|
250
|
+
|--------|---------|
|
|
251
|
+
| `to_s` | `"Path(7): 40.70... -> 40.71... -> ..."` |
|
|
252
|
+
| `inspect` | `"#<Geodetic::Path size=7 first=... last=...>"` |
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Feature Integration
|
|
257
|
+
|
|
258
|
+
A Path can be used as the geometry of a `Feature`. When a Feature wraps a Path, `distance_to` and `bearing_to` use the Path's geometric projection to find the closest approach point.
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
hiking_route = Feature.new(
|
|
262
|
+
label: "Manhattan Walking Tour",
|
|
263
|
+
geometry: route,
|
|
264
|
+
metadata: { type: "walking" }
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
hiking_route.distance_to(statue_of_liberty).to_km
|
|
268
|
+
hiking_route.bearing_to(statue_of_liberty).to_compass
|
|
269
|
+
```
|