geodetic 0.2.0 → 0.3.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/CHANGELOG.md +30 -0
- data/README.md +83 -28
- data/docs/coordinate-systems/gars.md +0 -4
- data/docs/coordinate-systems/georef.md +0 -4
- data/docs/coordinate-systems/gh.md +0 -4
- data/docs/coordinate-systems/gh36.md +0 -4
- data/docs/coordinate-systems/h3.md +308 -0
- data/docs/coordinate-systems/ham.md +0 -4
- data/docs/coordinate-systems/index.md +25 -23
- data/docs/coordinate-systems/olc.md +0 -4
- data/docs/index.md +7 -3
- data/docs/reference/conversions.md +15 -15
- data/docs/reference/feature.md +116 -0
- data/docs/reference/map-rendering.md +32 -0
- data/docs/reference/serialization.md +4 -4
- data/examples/02_all_coordinate_systems.rb +0 -3
- data/examples/03_distance_calculations.rb +1 -0
- data/examples/04_bearing_calculations.rb +1 -0
- data/examples/05_map_rendering/.gitignore +2 -0
- data/examples/05_map_rendering/demo.rb +264 -0
- data/examples/05_map_rendering/icons/bridge.png +0 -0
- data/examples/05_map_rendering/icons/building.png +0 -0
- data/examples/05_map_rendering/icons/landmark.png +0 -0
- data/examples/05_map_rendering/icons/monument.png +0 -0
- data/examples/05_map_rendering/icons/park.png +0 -0
- data/examples/05_map_rendering/nyc_landmarks.png +0 -0
- data/examples/README.md +62 -0
- data/fiddle_pointer_buffer_pool.md +119 -0
- data/lib/geodetic/coordinate/bng.rb +14 -33
- data/lib/geodetic/coordinate/ecef.rb +5 -1
- data/lib/geodetic/coordinate/enu.rb +13 -0
- data/lib/geodetic/coordinate/gars.rb +2 -3
- data/lib/geodetic/coordinate/georef.rb +2 -3
- data/lib/geodetic/coordinate/gh.rb +2 -4
- data/lib/geodetic/coordinate/gh36.rb +4 -5
- data/lib/geodetic/coordinate/h3.rb +412 -0
- data/lib/geodetic/coordinate/ham.rb +2 -3
- data/lib/geodetic/coordinate/lla.rb +15 -1
- data/lib/geodetic/coordinate/mgrs.rb +1 -1
- data/lib/geodetic/coordinate/ned.rb +13 -0
- data/lib/geodetic/coordinate/olc.rb +0 -1
- data/lib/geodetic/coordinate/spatial_hash.rb +2 -2
- data/lib/geodetic/coordinate/state_plane.rb +9 -0
- data/lib/geodetic/coordinate/ups.rb +1 -1
- data/lib/geodetic/coordinate/usng.rb +1 -1
- data/lib/geodetic/coordinate/utm.rb +1 -1
- data/lib/geodetic/coordinate/web_mercator.rb +1 -1
- data/lib/geodetic/coordinate.rb +31 -26
- data/lib/geodetic/feature.rb +44 -0
- data/lib/geodetic/geoid_height.rb +11 -6
- data/lib/geodetic/version.rb +1 -1
- data/lib/geodetic.rb +1 -0
- data/mkdocs.yml +2 -0
- metadata +20 -5
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Coordinate Systems Overview
|
|
2
2
|
|
|
3
|
-
The Geodetic gem supports
|
|
3
|
+
The Geodetic gem supports 18 coordinate systems organized into six categories. All coordinate classes live under `Geodetic::Coordinate`.
|
|
4
4
|
|
|
5
5
|
## Global Systems
|
|
6
6
|
|
|
@@ -46,6 +46,7 @@ The Geodetic gem supports 17 coordinate systems organized into six categories. A
|
|
|
46
46
|
| **OLC** | `Geodetic::Coordinate::OLC` | Open Location Code (Plus Codes). Google's open system for encoding locations into short codes like `849VCWC8+R9`. Uses a 20-character alphabet with 5 paired levels of base-20 encoding plus optional grid refinement. Includes a `+` separator at position 8. Supports neighbor lookup, area extraction, and configurable precision (default 10 characters for ~14 m resolution). |
|
|
47
47
|
| **GEOREF** | `Geodetic::Coordinate::GEOREF` | World Geographic Reference System. A geocode system used in aviation and military applications that encodes positions using letter tiles (15° grid), letter degree subdivisions, and numeric minute pairs. Uses a 24-letter alphabet (A-Z excluding I and O). Supports variable precision from 15° tiles (2 chars) down to 0.01-minute resolution (12 chars). Default precision is 8 characters (1-minute resolution). |
|
|
48
48
|
| **GARS** | `Geodetic::Coordinate::GARS` | Global Area Reference System. An NGA standard that divides the world into 30-minute cells identified by a 3-digit longitude band (001-720) and 2-letter latitude band. Cells are subdivided into 15-minute quadrants (1-4) and 5-minute keypads (1-9, telephone layout). Variable precision: 5 chars (30'), 6 chars (15'), 7 chars (5'). Default precision is 7 characters. |
|
|
49
|
+
| **H3** | `Geodetic::Coordinate::H3` | H3 Hexagonal Hierarchical Index. Uber's spatial indexing system that divides the globe into hexagonal cells (and 12 pentagons) at 16 resolution levels (0-15). Each cell is a 64-bit integer displayed as a hex string. Unlike the rectangular spatial hashes, `to_area` returns an `Areas::Polygon` with 6 vertices (5 for pentagons) and `neighbors` returns an Array of 6 cells. **Requires `libh3` installed** (`brew install h3` on macOS). |
|
|
49
50
|
|
|
50
51
|
## Regional Systems
|
|
51
52
|
|
|
@@ -60,31 +61,32 @@ The Geodetic gem supports 17 coordinate systems organized into six categories. A
|
|
|
60
61
|
|
|
61
62
|
Every coordinate system can convert to every other coordinate system. The table below confirms full interoperability:
|
|
62
63
|
|
|
63
|
-
| From \ To | LLA | ECEF | UTM | ENU | NED | MGRS | USNG | WebMercator | UPS | StatePlane | BNG | GH36 | GH | HAM | OLC | GEOREF | GARS |
|
|
64
|
-
|
|
65
|
-
| **LLA** | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
66
|
-
| **ECEF** | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
67
|
-
| **UTM** | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
68
|
-
| **ENU** | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
69
|
-
| **NED** | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
70
|
-
| **MGRS** | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
71
|
-
| **USNG** | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
72
|
-
| **WebMercator**| Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
73
|
-
| **UPS** | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
74
|
-
| **StatePlane** | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y |
|
|
75
|
-
| **BNG** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y |
|
|
76
|
-
| **GH36** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y |
|
|
77
|
-
| **GH** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y |
|
|
78
|
-
| **HAM** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y |
|
|
79
|
-
| **OLC** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y |
|
|
80
|
-
| **GEOREF** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y |
|
|
81
|
-
| **GARS** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- |
|
|
64
|
+
| From \ To | LLA | ECEF | UTM | ENU | NED | MGRS | USNG | WebMercator | UPS | StatePlane | BNG | GH36 | GH | HAM | OLC | GEOREF | GARS | H3 |
|
|
65
|
+
|-----------|-----|------|-----|-----|-----|------|------|-------------|-----|------------|-----|------|----|----|-----|--------|------|-----|
|
|
66
|
+
| **LLA** | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
67
|
+
| **ECEF** | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
68
|
+
| **UTM** | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
69
|
+
| **ENU** | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
70
|
+
| **NED** | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
71
|
+
| **MGRS** | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
72
|
+
| **USNG** | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
73
|
+
| **WebMercator**| Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
74
|
+
| **UPS** | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
75
|
+
| **StatePlane** | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y | Y |
|
|
76
|
+
| **BNG** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y | Y |
|
|
77
|
+
| **GH36** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y | Y |
|
|
78
|
+
| **GH** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y | Y |
|
|
79
|
+
| **HAM** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y | Y |
|
|
80
|
+
| **OLC** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y | Y |
|
|
81
|
+
| **GEOREF** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y | Y |
|
|
82
|
+
| **GARS** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- | Y |
|
|
83
|
+
| **H3** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -- |
|
|
82
84
|
|
|
83
85
|
## Universal Distance and Bearing Calculations
|
|
84
86
|
|
|
85
|
-
All coordinate systems support universal distance calculations via `distance_to` (Vincenty great-circle) and `straight_line_distance_to` (ECEF Euclidean). These methods work across different coordinate types -- for example, computing the distance from a UTM coordinate to an MGRS coordinate. Class-level methods `
|
|
87
|
+
All coordinate systems support universal distance calculations via `distance_to` (Vincenty great-circle) and `straight_line_distance_to` (ECEF Euclidean). These methods work across different coordinate types -- for example, computing the distance from a UTM coordinate to an MGRS coordinate. Class-level methods `Geodetic::Coordinate.distance_between` and `Geodetic::Coordinate.straight_line_distance_between` compute consecutive chain distances across a sequence of coordinates.
|
|
86
88
|
|
|
87
|
-
All coordinate systems also support universal bearing calculations via `bearing_to` (great-circle forward azimuth) and `elevation_to` (vertical look angle). These return `Bearing` and `Float` objects respectively. The class-level method `
|
|
89
|
+
All coordinate systems also support universal bearing calculations via `bearing_to` (great-circle forward azimuth) and `elevation_to` (vertical look angle). These return `Bearing` and `Float` objects respectively. The class-level method `Geodetic::Coordinate.bearing_between` computes consecutive chain bearings.
|
|
88
90
|
|
|
89
91
|
See the [Conversions Reference](../reference/conversions.md#distance-calculations) for details on distances and [Bearing Calculations](../reference/conversions.md#bearing-calculations) for bearings.
|
|
90
92
|
|
|
@@ -98,6 +100,6 @@ Conversions typically route through **LLA** or **ECEF** as intermediate steps:
|
|
|
98
100
|
- **ECEF** is the intermediate for local tangent plane systems (ENU, NED), since the rotation from global Cartesian to local frames is straightforward in ECEF.
|
|
99
101
|
- **ENU and NED** convert between each other directly by reordering axes and inverting the vertical component.
|
|
100
102
|
- **MGRS and USNG** route through UTM, which in turn routes through LLA.
|
|
101
|
-
- **WebMercator, UPS, BNG, StatePlane, GH36, GH, HAM, OLC, GEOREF, and
|
|
103
|
+
- **WebMercator, UPS, BNG, StatePlane, GH36, GH, HAM, OLC, GEOREF, GARS, and H3** all convert through LLA.
|
|
102
104
|
|
|
103
105
|
For example, converting from BNG to NED follows the chain: `BNG -> LLA -> ECEF -> ENU -> NED`. The gem handles this automatically when you call a conversion method.
|
data/docs/index.md
CHANGED
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
<td width="50%" valign="top">
|
|
10
10
|
<h2>Key Features</h2>
|
|
11
11
|
<lu>
|
|
12
|
-
<li><strong>
|
|
12
|
+
<li><strong>18 Coordinate Systems</strong> - LLA, ECEF, UTM, ENU, NED, MGRS, USNG, Web Mercator, UPS, State Plane, BNG, GH36, GH, HAM, OLC<br>
|
|
13
13
|
<li><strong>Full Bidirectional Conversions</strong> - Every system converts to and from every other system<br>
|
|
14
14
|
<li><strong>Distance Calculations</strong> - Vincenty great-circle and straight-line with unit tracking<br>
|
|
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>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
|
|
18
19
|
<li><strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
19
20
|
<li><strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
|
|
20
21
|
<li><strong>Multiple Datums</strong> - WGS84, Clarke 1866, GRS 1980, Airy 1830, and more<br>
|
|
@@ -24,11 +25,11 @@
|
|
|
24
25
|
</tr>
|
|
25
26
|
</table>
|
|
26
27
|
|
|
27
|
-
Geodetic is a Ruby gem for converting between geodetic coordinate systems. It provides a clean, consistent API for working with
|
|
28
|
+
Geodetic is a Ruby gem for converting between geodetic coordinate systems. It provides a clean, consistent API for working with 18 coordinate systems, 16 geodetic datums, geoid height calculations, and geographic area computations.
|
|
28
29
|
|
|
29
30
|
## Coordinate Systems
|
|
30
31
|
|
|
31
|
-
Geodetic supports full bidirectional conversion between all
|
|
32
|
+
Geodetic supports full bidirectional conversion between all 18 coordinate systems:
|
|
32
33
|
|
|
33
34
|
| System | Class | Description |
|
|
34
35
|
|--------|-------|-------------|
|
|
@@ -49,18 +50,21 @@ Geodetic supports full bidirectional conversion between all 17 coordinate system
|
|
|
49
50
|
| **OLC** | `Geodetic::Coordinate::OLC` | Open Location Code / Plus Codes (Google's location encoding) |
|
|
50
51
|
| **GEOREF** | `Geodetic::Coordinate::GEOREF` | World Geographic Reference System (aviation/military) |
|
|
51
52
|
| **GARS** | `Geodetic::Coordinate::GARS` | Global Area Reference System (NGA standard) |
|
|
53
|
+
| **H3** | `Geodetic::Coordinate::H3` | Uber's hexagonal hierarchical index (requires `libh3`) |
|
|
52
54
|
|
|
53
55
|
## Additional Features
|
|
54
56
|
|
|
55
57
|
- **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.
|
|
56
58
|
- **Geoid height calculations** -- Convert between ellipsoidal and orthometric heights using models such as EGM96, EGM2008, GEOID18, and GEOID12B.
|
|
57
59
|
- **Geographic areas** -- `Geodetic::Areas::Circle`, `Geodetic::Areas::Polygon`, and `Geodetic::Areas::Rectangle` for point-in-area testing.
|
|
60
|
+
- **Features** -- `Geodetic::Feature` wraps any coordinate or area with a label and metadata hash, delegating `distance_to` and `bearing_to` to the underlying geometry.
|
|
58
61
|
|
|
59
62
|
## Design Principles
|
|
60
63
|
|
|
61
64
|
- All constructors use **keyword arguments** for clarity.
|
|
62
65
|
- Every coordinate system supports **serialization** via `to_s` and `to_a`, and **deserialization** via `from_string` and `from_array`.
|
|
63
66
|
- Conversions are available as instance methods (`to_ecef`, `to_utm`, etc.) and class-level factory methods (`from_ecef`, `from_utm`, etc.).
|
|
67
|
+
- All registered coordinate systems are discoverable at runtime via `Geodetic::Coordinate.systems`.
|
|
64
68
|
|
|
65
69
|
## Quick Example
|
|
66
70
|
|
|
@@ -240,31 +240,31 @@ Universal distance methods are available on all coordinate types and work across
|
|
|
240
240
|
### Great-Circle Distance (Vincenty)
|
|
241
241
|
|
|
242
242
|
- **`distance_to(other, *others)`** — Instance method. Computes the Vincenty great-circle distance from the receiver to one or more target coordinates. Returns a `Distance` for a single target, or an Array of `Distance` objects for multiple targets (radial distances from the receiver).
|
|
243
|
-
- **`
|
|
243
|
+
- **`Geodetic::Coordinate.distance_between(*coords)`** — Class method on `Geodetic::Coordinate`. Computes consecutive chain distances between an ordered sequence of coordinates. Returns a `Distance` for two coordinates, or an Array of `Distance` objects for three or more.
|
|
244
244
|
|
|
245
245
|
> **`Distance` objects** wrap a distance value and provide unit-aware access. Call `.meters` to get the raw Float value in meters, or `.to_f` to get the value in the current display unit.
|
|
246
246
|
|
|
247
247
|
```ruby
|
|
248
|
-
seattle =
|
|
249
|
-
portland =
|
|
250
|
-
sf =
|
|
248
|
+
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
|
|
249
|
+
portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
|
|
250
|
+
sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
|
|
251
251
|
|
|
252
252
|
# Radial distances from receiver
|
|
253
253
|
seattle.distance_to(portland) # => Distance (235393.17 m)
|
|
254
254
|
seattle.distance_to(portland, sf) # => [Distance, Distance] (Array)
|
|
255
255
|
|
|
256
256
|
# Consecutive chain distances
|
|
257
|
-
|
|
257
|
+
Geodetic::Coordinate.distance_between(seattle, portland, sf) # => [Distance, Distance] (Array)
|
|
258
258
|
```
|
|
259
259
|
|
|
260
260
|
### Straight-Line Distance (ECEF Euclidean)
|
|
261
261
|
|
|
262
262
|
- **`straight_line_distance_to(other, *others)`** — Instance method. Computes the Euclidean distance in ECEF (3D Cartesian) space. Returns a `Distance` for a single target, or an Array of `Distance` objects for multiple targets.
|
|
263
|
-
- **`
|
|
263
|
+
- **`Geodetic::Coordinate.straight_line_distance_between(*coords)`** — Class method. Computes consecutive chain Euclidean distances.
|
|
264
264
|
|
|
265
265
|
```ruby
|
|
266
266
|
seattle.straight_line_distance_to(portland) # => Distance
|
|
267
|
-
|
|
267
|
+
Geodetic::Coordinate.straight_line_distance_between(seattle, portland) # => Distance
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
### Cross-System Distances
|
|
@@ -273,7 +273,7 @@ Both `distance_to` and `straight_line_distance_to` accept any coordinate type. C
|
|
|
273
273
|
|
|
274
274
|
```ruby
|
|
275
275
|
utm = seattle.to_utm
|
|
276
|
-
mgrs =
|
|
276
|
+
mgrs = Geodetic::Coordinate::MGRS.from_lla(portland)
|
|
277
277
|
utm.distance_to(mgrs) # => Distance (235393.17 m)
|
|
278
278
|
```
|
|
279
279
|
|
|
@@ -282,7 +282,7 @@ utm.distance_to(mgrs) # => Distance (235393.17 m)
|
|
|
282
282
|
ENU and NED are relative coordinate systems and do not support `distance_to` or `straight_line_distance_to` directly. Convert to an absolute system first:
|
|
283
283
|
|
|
284
284
|
```ruby
|
|
285
|
-
ref =
|
|
285
|
+
ref = Geodetic::Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 0.0)
|
|
286
286
|
lla = enu.to_lla(ref)
|
|
287
287
|
lla.distance_to(other_lla)
|
|
288
288
|
```
|
|
@@ -299,12 +299,12 @@ Universal bearing methods are available on all coordinate types and work across
|
|
|
299
299
|
|
|
300
300
|
- **`bearing_to(other)`** — Instance method. Computes the great-circle forward azimuth from the receiver to the target coordinate. Returns a `Bearing` object.
|
|
301
301
|
- **`elevation_to(other)`** — Instance method. Computes the vertical look angle (elevation) from the receiver to the target. Returns a Float in degrees (-90 to +90).
|
|
302
|
-
- **`
|
|
302
|
+
- **`Geodetic::Coordinate.bearing_between(*coords)`** — Class method on `Geodetic::Coordinate`. Computes consecutive chain bearings between an ordered sequence of coordinates. Returns a `Bearing` for two coordinates, or an Array of `Bearing` objects for three or more.
|
|
303
303
|
|
|
304
304
|
```ruby
|
|
305
|
-
seattle =
|
|
306
|
-
portland =
|
|
307
|
-
sf =
|
|
305
|
+
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
|
|
306
|
+
portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0.0)
|
|
307
|
+
sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0.0)
|
|
308
308
|
|
|
309
309
|
# Forward azimuth
|
|
310
310
|
b = seattle.bearing_to(portland) # => Bearing
|
|
@@ -316,7 +316,7 @@ b.reverse # => Bearing (back azimuth)
|
|
|
316
316
|
seattle.elevation_to(portland) # => Float (degrees)
|
|
317
317
|
|
|
318
318
|
# Consecutive chain bearings
|
|
319
|
-
|
|
319
|
+
Geodetic::Coordinate.bearing_between(seattle, portland, sf) # => [Bearing, Bearing]
|
|
320
320
|
```
|
|
321
321
|
|
|
322
322
|
### Cross-System Bearings
|
|
@@ -325,7 +325,7 @@ GCS.bearing_between(seattle, portland, sf) # => [Bearing, Bearing]
|
|
|
325
325
|
|
|
326
326
|
```ruby
|
|
327
327
|
utm = seattle.to_utm
|
|
328
|
-
mgrs =
|
|
328
|
+
mgrs = Geodetic::Coordinate::MGRS.from_lla(portland)
|
|
329
329
|
utm.bearing_to(mgrs) # => Bearing
|
|
330
330
|
```
|
|
331
331
|
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Feature Reference
|
|
2
|
+
|
|
3
|
+
`Geodetic::Feature` wraps a geometry with a human-readable label and an arbitrary metadata hash. It provides a single object that ties spatial data to application-level information like names, categories, and display properties.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Constructor
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
Feature.new(
|
|
11
|
+
label: "Statue of Liberty",
|
|
12
|
+
geometry: Geodetic::Coordinate::LLA.new(lat: 40.6892, lng: -74.0445, alt: 0),
|
|
13
|
+
metadata: { category: "monument", year: 1886 }
|
|
14
|
+
)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The `geometry` parameter accepts any coordinate class or any area class (`Circle`, `Polygon`, `Rectangle`). The `metadata` hash is optional and defaults to `{}`.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Attributes
|
|
22
|
+
|
|
23
|
+
| Attribute | Type | Mutable | Description |
|
|
24
|
+
|------------|--------|---------|-------------|
|
|
25
|
+
| `label` | String | Yes | A display name for the feature |
|
|
26
|
+
| `geometry` | Object | Yes | A coordinate or area object |
|
|
27
|
+
| `metadata` | Hash | Yes | Arbitrary key-value pairs |
|
|
28
|
+
|
|
29
|
+
All three attributes have both reader and writer methods.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Geometry Types
|
|
34
|
+
|
|
35
|
+
A Feature's geometry can be any of:
|
|
36
|
+
|
|
37
|
+
- **Coordinate** — any of the 18 coordinate classes (`LLA`, `ECEF`, `UTM`, etc.)
|
|
38
|
+
- **Area** — `Areas::Circle`, `Areas::Polygon`, or `Areas::Rectangle`
|
|
39
|
+
|
|
40
|
+
When the geometry is an area, `distance_to` and `bearing_to` use the area's `centroid` as the reference point.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Methods
|
|
45
|
+
|
|
46
|
+
### `distance_to(other)`
|
|
47
|
+
|
|
48
|
+
Returns a `Geodetic::Distance` between this feature and another feature, coordinate, or area. When either side is an area geometry, its centroid is used.
|
|
49
|
+
|
|
50
|
+
The `other` parameter can be a `Feature`, a coordinate, or an area.
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
liberty = Feature.new(label: "Liberty", geometry: LLA.new(lat: 40.6892, lng: -74.0445, alt: 0))
|
|
54
|
+
empire = Feature.new(label: "Empire", geometry: LLA.new(lat: 40.7484, lng: -73.9857, alt: 0))
|
|
55
|
+
|
|
56
|
+
liberty.distance_to(empire).to_km # => "8.24 km"
|
|
57
|
+
|
|
58
|
+
# Also works with a raw coordinate
|
|
59
|
+
liberty.distance_to(LLA.new(lat: 40.7484, lng: -73.9857, alt: 0))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `bearing_to(other)`
|
|
63
|
+
|
|
64
|
+
Returns a `Geodetic::Bearing` from this feature to another feature, coordinate, or area. Uses the same centroid resolution as `distance_to`.
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
liberty.bearing_to(empire).degrees # => 36.99
|
|
68
|
+
liberty.bearing_to(empire).to_compass # => "NE"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `to_s`
|
|
72
|
+
|
|
73
|
+
Returns `"label (geometry.to_s)"`.
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
liberty.to_s # => "Liberty (40.689200, -74.044500, 0.00)"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### `inspect`
|
|
80
|
+
|
|
81
|
+
Returns a detailed string with label, geometry, and metadata.
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
liberty.inspect
|
|
85
|
+
# => "#<Geodetic::Feature name=\"Liberty\" geometry=#<Geodetic::Coordinate::LLA ...> metadata={}>"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Area Geometry Example
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
park_boundary = Areas::Polygon.new(boundary: [
|
|
94
|
+
LLA.new(lat: 40.7679, lng: -73.9818, alt: 0),
|
|
95
|
+
LLA.new(lat: 40.7649, lng: -73.9727, alt: 0),
|
|
96
|
+
LLA.new(lat: 40.8003, lng: -73.9494, alt: 0),
|
|
97
|
+
LLA.new(lat: 40.8008, lng: -73.9585, alt: 0),
|
|
98
|
+
])
|
|
99
|
+
|
|
100
|
+
park = Feature.new(label: "Central Park", geometry: park_boundary)
|
|
101
|
+
|
|
102
|
+
# distance_to uses the polygon's centroid
|
|
103
|
+
park.distance_to(liberty) # => Distance
|
|
104
|
+
park.bearing_to(liberty) # => Bearing
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Centroid Resolution
|
|
110
|
+
|
|
111
|
+
When computing distances and bearings, Feature resolves the underlying point as follows:
|
|
112
|
+
|
|
113
|
+
- If the geometry responds to `centroid` (all area classes do), the centroid is used.
|
|
114
|
+
- Otherwise, the geometry itself is used directly (all coordinate classes).
|
|
115
|
+
|
|
116
|
+
This applies to both the source feature and the target. When the target is a Feature, its geometry is resolved the same way. When the target is a raw coordinate or area, the same logic applies.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Map Rendering with libgd-gis
|
|
2
|
+
|
|
3
|
+
Geodetic coordinates and areas can be rendered on raster maps using the [libgd-gis](https://rubygems.org/gems/libgd-gis) gem, which provides tile-based basemap rendering on top of [ruby-libgd](https://rubygems.org/gems/ruby-libgd).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `libgd-gis` gem downloads map tiles and stitches them into a single raster image for a given bounding box and zoom level. Geodetic's coordinate objects provide the geographic data, and `GD::GIS::Geometry.project` converts longitude/latitude pairs into pixel positions on the rendered map. From there, ruby-libgd primitives (lines, circles, polygons, text, image compositing) can draw overlays on top of the basemap.
|
|
8
|
+
|
|
9
|
+
This combination supports:
|
|
10
|
+
|
|
11
|
+
- **Point markers** for any Geodetic coordinate
|
|
12
|
+
- **Polygon overlays** from `Geodetic::Areas::Polygon` boundaries
|
|
13
|
+
- **Bearing arrows** computed with `Feature#bearing_to` and drawn as lines with arrowheads
|
|
14
|
+
- **Distance labels** using `Feature#distance_to` for annotation
|
|
15
|
+
- **Icon compositing** with scaled PNG images positioned at projected coordinates
|
|
16
|
+
- **Light and dark basemaps** via `:carto_light` and `:carto_dark`
|
|
17
|
+
|
|
18
|
+
## Feature Class
|
|
19
|
+
|
|
20
|
+
`Geodetic::Feature` wraps a geometry (any coordinate or area) with a label and a metadata hash. It delegates `distance_to` and `bearing_to` to its geometry, using the centroid for area geometries. This makes it straightforward to attach display properties like icon paths and categories alongside the spatial data.
|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
gem install libgd-gis
|
|
26
|
+
brew install gd # macOS
|
|
27
|
+
# apt install libgd-dev # Linux
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Example
|
|
31
|
+
|
|
32
|
+
See [`examples/05_map_rendering/demo.rb`](https://github.com/madbomber/geodetic/tree/main/examples/05_map_rendering) for a complete working demo that renders NYC landmarks with icons, a Central Park polygon boundary, and bearing arrows between landmarks. The demo supports light/dark themes, icon scaling, and CLI flags for toggling features.
|
|
@@ -254,7 +254,7 @@ restored = Geodetic::Coordinate::LLA.from_dms(dms_string)
|
|
|
254
254
|
| StatePlane | `easting, northing, zone_code` | 2 | `[easting, northing, zone_code]` | -- |
|
|
255
255
|
| MGRS | `grid_zone+square+coords` | n/a | `[grid_zone, square_id, easting, northing, precision]` | String-based |
|
|
256
256
|
| USNG | `grid_zone square coords` | n/a | `[grid_zone, square_id, easting, northing, precision]` | `to_full_format`, `to_abbreviated_format` |
|
|
257
|
-
| GH36 | geohash string | n/a | `[lat, lng]` | `
|
|
258
|
-
| GH | geohash string | n/a | `[lat, lng]` | `
|
|
259
|
-
| HAM | locator string | n/a | `[lat, lng]` | `
|
|
260
|
-
| OLC | plus code string | n/a | `[lat, lng]` | `
|
|
257
|
+
| GH36 | geohash string | n/a | `[lat, lng]` | `to_area`, `neighbors` |
|
|
258
|
+
| GH | geohash string | n/a | `[lat, lng]` | `to_area`, `neighbors` |
|
|
259
|
+
| HAM | locator string | n/a | `[lat, lng]` | `to_area`, `neighbors` |
|
|
260
|
+
| OLC | plus code string | n/a | `[lat, lng]` | `to_area`, `neighbors` |
|
|
@@ -161,9 +161,6 @@ def demo_coordinate_systems
|
|
|
161
161
|
puts " Precision: #{gh36_coord.precision} chars"
|
|
162
162
|
puts " Precision in meters: lat=#{gh36_coord.precision_in_meters[:lat].round(3)}m, lng=#{gh36_coord.precision_in_meters[:lng].round(3)}m"
|
|
163
163
|
|
|
164
|
-
# URL slug
|
|
165
|
-
puts " URL slug: #{gh36_coord.to_slug}"
|
|
166
|
-
|
|
167
164
|
# Reduced precision
|
|
168
165
|
gh36_short = GH36.new(lla_coord, precision: 5)
|
|
169
166
|
puts " 5-char precision: #{gh36_short.to_s}"
|