geodetic 0.3.2 → 0.5.0
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 +84 -2
- data/README.md +121 -8
- data/docs/coordinate-systems/gars.md +2 -2
- data/docs/coordinate-systems/georef.md +2 -2
- data/docs/coordinate-systems/gh.md +2 -2
- data/docs/coordinate-systems/gh36.md +2 -2
- data/docs/coordinate-systems/h3.md +2 -2
- data/docs/coordinate-systems/ham.md +2 -2
- data/docs/coordinate-systems/olc.md +2 -2
- data/docs/index.md +4 -2
- data/docs/reference/areas.md +140 -14
- data/docs/reference/arithmetic.md +368 -0
- data/docs/reference/feature.md +2 -2
- data/docs/reference/path.md +3 -3
- data/docs/reference/segment.md +181 -0
- data/docs/reference/vector.md +256 -0
- data/examples/02_all_coordinate_systems.rb +6 -6
- data/examples/06_path_operations.rb +2 -4
- data/examples/07_segments_and_shapes.rb +258 -0
- data/examples/08_geodetic_arithmetic.rb +393 -0
- data/examples/README.md +35 -1
- data/lib/geodetic/areas/bounding_box.rb +56 -0
- data/lib/geodetic/areas/circle.rb +8 -0
- data/lib/geodetic/areas/hexagon.rb +11 -0
- data/lib/geodetic/areas/octagon.rb +11 -0
- data/lib/geodetic/areas/pentagon.rb +11 -0
- data/lib/geodetic/areas/polygon.rb +64 -14
- data/lib/geodetic/areas/rectangle.rb +85 -35
- data/lib/geodetic/areas/regular_polygon.rb +59 -0
- data/lib/geodetic/areas/triangle.rb +180 -0
- data/lib/geodetic/areas.rb +6 -0
- data/lib/geodetic/coordinate/gh36.rb +1 -1
- data/lib/geodetic/coordinate/h3.rb +1 -1
- data/lib/geodetic/coordinate/spatial_hash.rb +2 -2
- data/lib/geodetic/coordinate.rb +26 -1
- data/lib/geodetic/distance.rb +5 -1
- data/lib/geodetic/path.rb +85 -153
- data/lib/geodetic/segment.rb +193 -0
- data/lib/geodetic/vector.rb +242 -0
- data/lib/geodetic/version.rb +1 -1
- data/lib/geodetic.rb +2 -0
- data/mkdocs.yml +1 -0
- metadata +14 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 495ba0272532d5591ecbb37869857675af00014d6863849c79c6fbef35e590c3
|
|
4
|
+
data.tar.gz: dcb68a4385f7a888fbfb37b421d2f82c66aae077bda8557ce2a7b60c57e38b35
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f276d8924aad177efc94aee0037f1fbd4e1cb655668ed54ff5ba0e471c16dd91687966577505057e31a144ea545adf3d8843535ca0705fca69fc5e5bf9a3637
|
|
7
|
+
data.tar.gz: f2d8102549b1502099d149453f5b18d8143b5799e3d3f5c2c4f947c86b7e2de1355e74916ae3b0d7376652fdb547ebd48488bff2eba18d60211da4ff0ff030a3
|
data/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
## [0.5.0] - 2026-03-10
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`Geodetic::Vector` class** — geodetic displacement pairing a Distance (magnitude) with a Bearing (direction)
|
|
19
|
+
- **Construction**: `Vector.new(distance:, bearing:)` with automatic coercion from numeric values
|
|
20
|
+
- **Components**: `north`, `east` — decomposed meters; `magnitude` — distance in meters
|
|
21
|
+
- **Factory methods**: `Vector.from_components(north:, east:)`, `Vector.from_segment(segment)`
|
|
22
|
+
- **Vincenty direct**: `destination_from(origin)` solves the direct geodetic problem on the WGS84 ellipsoid
|
|
23
|
+
- **Arithmetic**: `+`, `-` (component-wise), `*`, `/` (scalar), `-@` (unary minus); `Numeric * Vector` via coerce
|
|
24
|
+
- **Products**: `dot(other)`, `cross(other)`, `angle_between(other)`
|
|
25
|
+
- **Properties**: `zero?`, `normalize`, `reverse`/`inverse`
|
|
26
|
+
- **Comparable**: ordered by distance (magnitude)
|
|
27
|
+
- Near-zero results (< 1e-9 m) snap to clean zero vector
|
|
28
|
+
- **Geodetic arithmetic with `+` operator** — build geometry from coordinates, vectors, and distances:
|
|
29
|
+
- `Coordinate + Coordinate` → Segment
|
|
30
|
+
- `Coordinate + Coordinate + Coordinate` → Path (via Segment + Coordinate → Path)
|
|
31
|
+
- `Coordinate + Segment` → Path
|
|
32
|
+
- `Segment + Coordinate` → Path
|
|
33
|
+
- `Segment + Segment` → Path
|
|
34
|
+
- `Coordinate + Distance` → Circle
|
|
35
|
+
- `Distance + Coordinate` → Circle (commutative)
|
|
36
|
+
- `Coordinate + Vector` → Segment (Vincenty direct)
|
|
37
|
+
- `Vector + Coordinate` → Segment (reverse start to coordinate)
|
|
38
|
+
- `Segment + Vector` → Path (extend from endpoint)
|
|
39
|
+
- `Vector + Segment` → Path (prepend via reverse)
|
|
40
|
+
- `Path + Vector` → Path (extend from last point)
|
|
41
|
+
- **Translation with `*` operator and `translate` method** — uniform displacement across all geometric types:
|
|
42
|
+
- `Coordinate * Vector` → Coordinate (translated point)
|
|
43
|
+
- `Segment * Vector` → Segment (translated endpoints)
|
|
44
|
+
- `Path * Vector` → Path (translated waypoints)
|
|
45
|
+
- `Circle * Vector` → Circle (translated centroid, preserved radius)
|
|
46
|
+
- `Polygon * Vector` → Polygon (translated vertices)
|
|
47
|
+
- **`Segment#to_vector`** — extract a Vector from a Segment's length and bearing
|
|
48
|
+
- **`Path#to_corridor(width:)`** — convert a path into a Polygon corridor of a given width; uses mean bearing at interior waypoints to avoid self-intersection; accepts meters or a Distance object
|
|
49
|
+
- **Geodetic arithmetic example** (`examples/08_geodetic_arithmetic.rb`) — 11-section demo covering all arithmetic operators, Vector class, translation, corridors, and composed operations
|
|
50
|
+
- Documentation: `docs/reference/vector.md` (Vector reference), `docs/reference/arithmetic.md` (Geodetic Arithmetic reference)
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
|
|
54
|
+
- Updated README with Vector, Geodetic Arithmetic, and Corridors sections; added to key features list
|
|
55
|
+
- Updated `examples/README.md` with example 08 description
|
|
56
|
+
|
|
57
|
+
## [0.4.0] - 2026-03-10
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- **`Geodetic::Segment` class** — directed two-point line segment, the fundamental geometric primitive underlying Path and Polygon
|
|
62
|
+
- **Properties**: `length`/`distance` (returns Distance), `length_meters`, `bearing` (returns Bearing), `midpoint`/`centroid` (returns LLA) — all lazily computed and cached
|
|
63
|
+
- **Projection**: `project(point)` returns the closest point on the segment and perpendicular distance
|
|
64
|
+
- **Interpolation**: `interpolate(fraction)` returns the LLA at any fraction along the segment
|
|
65
|
+
- **Membership**: `includes?(point)` (vertex-only check), `contains?(point, tolerance:)` (on-segment check), `excludes?`
|
|
66
|
+
- **Intersection**: `intersects?(other_segment)` using cross-product orientation tests
|
|
67
|
+
- **Conversion**: `reverse`, `to_path`, `to_a`, `==`, `to_s`, `inspect`
|
|
68
|
+
- **`Geodetic::Areas::Triangle`** — polygon subclass with four construction modes
|
|
69
|
+
- Isosceles: `Triangle.new(center:, width:, height:, bearing:)`
|
|
70
|
+
- Equilateral by circumradius: `Triangle.new(center:, radius:, bearing:)`
|
|
71
|
+
- Equilateral by side length: `Triangle.new(center:, side:, bearing:)`
|
|
72
|
+
- Arbitrary vertices: `Triangle.new(vertices: [p1, p2, p3])`
|
|
73
|
+
- Predicates: `equilateral?`, `isosceles?`, `scalene?` based on actual side lengths (5m tolerance)
|
|
74
|
+
- Methods: `vertices`, `side_lengths`, `base`, `to_bounding_box`
|
|
75
|
+
- **`Geodetic::Areas::Rectangle`** — polygon subclass defined by a centerline Segment and perpendicular width
|
|
76
|
+
- `Rectangle.new(segment:, width:)` — accepts a Segment object or a two-element array of coordinates
|
|
77
|
+
- `width:` accepts numeric (meters) or a Distance instance
|
|
78
|
+
- Derived properties: `center`, `height`, `bearing` from the centerline; `corners`, `square?`, `to_bounding_box`
|
|
79
|
+
- **`Geodetic::Areas::Pentagon`**, **`Hexagon`**, **`Octagon`** — regular polygon subclasses from center + radius + bearing
|
|
80
|
+
- **Polygon self-intersection validation** — `Polygon.new` validates that no edge crosses another; pass `validate: false` to skip (used by subclasses with generated geometry)
|
|
81
|
+
- **Polygon `segments` method** — returns `Array<Segment>` for each edge; `edges` and `border` are aliases
|
|
82
|
+
- **Segments and shapes example** (`examples/07_segments_and_shapes.rb`) — 10-section demo covering Segment operations, Triangle/Rectangle construction and predicates, regular polygons, containment, bounding boxes, and Feature integration
|
|
83
|
+
- Documentation: `docs/reference/segment.md` (Segment reference with Great Circle Arcs section), updated `docs/reference/areas.md` with all polygon subclasses
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- **Refactored `Path`** to use Segment objects — removed ~140 lines of private segment methods (`project_onto_segment`, `on_segment?`, `segments_intersect?`, `cross_sign`, `on_collinear?`, `to_flat`); all segment operations now delegate to Segment
|
|
88
|
+
- **Refactored `Polygon`** — `segments` is now the primary method (was `edges`); `edges` and `border` are aliases
|
|
89
|
+
- **Updated `Feature`** to support Segment as a geometry type via `centroid` (alias for `midpoint`)
|
|
90
|
+
- Updated README, `docs/index.md`, `docs/reference/areas.md`, `docs/reference/segment.md`, `examples/README.md`, and mkdocs nav
|
|
91
|
+
|
|
92
|
+
### Removed
|
|
93
|
+
|
|
94
|
+
- `Areas::Rectangle = Areas::BoundingBox` alias — Rectangle is now its own class (Polygon subclass)
|
|
95
|
+
|
|
14
96
|
## [0.3.2] - 2026-03-09
|
|
15
97
|
|
|
16
98
|
### Added
|
|
@@ -19,11 +101,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19
101
|
- **Navigation**: `first`, `last`, `next`, `prev`, `segments`, `size`, `empty?`
|
|
20
102
|
- **Membership**: `include?`/`includes?` (waypoint check), `contains?`/`inside?` (on-segment check with configurable tolerance)
|
|
21
103
|
- **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-
|
|
104
|
+
- **Closest points**: `closest_points_to` for Path-to-Path, Path-to-Polygon, Path-to-BoundingBox, and Path-to-Circle
|
|
23
105
|
- **Computed**: `total_distance`, `segment_distances`, `segment_bearings`, `reverse`
|
|
24
106
|
- **Subpath/split**: `between(from, to)` extracts a subpath; `split_at(coord)` divides into two paths sharing the split point
|
|
25
107
|
- **Interpolation**: `at_distance(distance)` finds the coordinate at a given distance along the path
|
|
26
|
-
- **Bounding box**: `bounds` returns an `Areas::
|
|
108
|
+
- **Bounding box**: `bounds` returns an `Areas::BoundingBox`
|
|
27
109
|
- **Polygon conversion**: `to_polygon` closes the path (validates no self-intersection)
|
|
28
110
|
- **Intersection**: `intersects?(other_path)` detects crossing segments
|
|
29
111
|
- **Equality**: `==` compares coordinates in order
|
data/README.md
CHANGED
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
- <strong>Distance Calculations</strong> - Vincenty great-circle and straight-line with unit tracking<br>
|
|
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
|
-
- <strong>Geographic Areas</strong> - Circle, Polygon,
|
|
21
|
+
- <strong>Geographic Areas</strong> - Circle, Polygon, BoundingBox, Triangle, Rectangle, Pentagon, Hexagon, Octagon<br>
|
|
22
|
+
- <strong>Segments</strong> - Directed two-point line segments with projection, intersection, and interpolation<br>
|
|
22
23
|
- <strong>Paths</strong> - Directed coordinate sequences with navigation, interpolation, closest approach, intersection, and area conversion<br>
|
|
23
24
|
- <strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
|
|
25
|
+
- <strong>Vectors</strong> - Geodetic displacement (distance + bearing) with full arithmetic and Vincenty direct<br>
|
|
26
|
+
- <strong>Geodetic Arithmetic</strong> - Compose geometry with operators: P1 + P2 → Segment, + P3 → Path, + Distance → Circle, * Vector → translate<br>
|
|
24
27
|
- <strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
25
28
|
- <strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
|
|
26
29
|
- <strong>Multiple Datums</strong> - WGS84, Clarke 1866, GRS 1980, Airy 1830, and more<br>
|
|
@@ -426,7 +429,7 @@ lla = gh36.to_lla
|
|
|
426
429
|
gh36.neighbors # => { N: GH36, S: GH36, E: GH36, W: GH36, NE: ..., NW: ..., SE: ..., SW: ... }
|
|
427
430
|
|
|
428
431
|
# Bounding rectangle of the geohash cell
|
|
429
|
-
area = gh36.to_area # => Areas::
|
|
432
|
+
area = gh36.to_area # => Areas::BoundingBox
|
|
430
433
|
area.includes?(gh36.to_lla) # => true
|
|
431
434
|
|
|
432
435
|
# Precision info
|
|
@@ -453,7 +456,7 @@ lla = gh.to_lla
|
|
|
453
456
|
gh.neighbors # => { N: GH, S: GH, E: GH, W: GH, NE: ..., NW: ..., SE: ..., SW: ... }
|
|
454
457
|
|
|
455
458
|
# Bounding rectangle of the geohash cell
|
|
456
|
-
area = gh.to_area # => Areas::
|
|
459
|
+
area = gh.to_area # => Areas::BoundingBox
|
|
457
460
|
area.includes?(gh.to_lla) # => true
|
|
458
461
|
|
|
459
462
|
# Precision info
|
|
@@ -480,7 +483,7 @@ lla = ham.to_lla
|
|
|
480
483
|
ham.neighbors # => { N: HAM, S: HAM, E: HAM, W: HAM, NE: ..., NW: ..., SE: ..., SW: ... }
|
|
481
484
|
|
|
482
485
|
# Bounding rectangle of the grid square
|
|
483
|
-
area = ham.to_area # => Areas::
|
|
486
|
+
area = ham.to_area # => Areas::BoundingBox
|
|
484
487
|
area.includes?(ham.to_lla) # => true
|
|
485
488
|
|
|
486
489
|
# Precision info
|
|
@@ -507,7 +510,7 @@ lla = olc.to_lla
|
|
|
507
510
|
olc.neighbors # => { N: OLC, S: OLC, E: OLC, W: OLC, NE: ..., NW: ..., SE: ..., SW: ... }
|
|
508
511
|
|
|
509
512
|
# Bounding rectangle of the plus code cell
|
|
510
|
-
area = olc.to_area # => Areas::
|
|
513
|
+
area = olc.to_area # => Areas::BoundingBox
|
|
511
514
|
area.includes?(olc.to_lla) # => true
|
|
512
515
|
|
|
513
516
|
# Precision info
|
|
@@ -532,16 +535,52 @@ points = [
|
|
|
532
535
|
polygon = Areas::Polygon.new(boundary: points)
|
|
533
536
|
polygon.centroid # => computed centroid as LLA
|
|
534
537
|
|
|
535
|
-
#
|
|
538
|
+
# BoundingBox area (accepts any coordinate type)
|
|
536
539
|
nw = Coordinates::LLA.new(lat: 41.0, lng: -75.0)
|
|
537
540
|
se = Coordinates::LLA.new(lat: 40.0, lng: -74.0)
|
|
538
|
-
rect = Areas::
|
|
541
|
+
rect = Areas::BoundingBox.new(nw: nw, se: se)
|
|
539
542
|
rect.centroid # => LLA at center
|
|
540
543
|
rect.ne # => computed NE corner
|
|
541
544
|
rect.sw # => computed SW corner
|
|
542
545
|
rect.includes?(point) # => true/false
|
|
543
546
|
```
|
|
544
547
|
|
|
548
|
+
### Segments
|
|
549
|
+
|
|
550
|
+
`Segment` represents a directed line segment between two points. It provides the geometric primitives that `Path` and `Polygon` build on.
|
|
551
|
+
|
|
552
|
+
```ruby
|
|
553
|
+
a = Coordinate::LLA.new(lat: 40.7484, lng: -73.9857, alt: 0)
|
|
554
|
+
b = Coordinate::LLA.new(lat: 40.7580, lng: -73.9855, alt: 0)
|
|
555
|
+
|
|
556
|
+
seg = Segment.new(a, b)
|
|
557
|
+
|
|
558
|
+
# Properties (lazily computed, cached)
|
|
559
|
+
seg.length # => Distance
|
|
560
|
+
seg.distance # => Distance (alias for length)
|
|
561
|
+
seg.bearing # => Bearing
|
|
562
|
+
seg.midpoint # => LLA at halfway point
|
|
563
|
+
|
|
564
|
+
# Projection — closest point on segment to a target
|
|
565
|
+
foot, dist_m = seg.project(target_point)
|
|
566
|
+
|
|
567
|
+
# Interpolation — point at fraction along segment
|
|
568
|
+
seg.interpolate(0.25) # => LLA at quarter-way
|
|
569
|
+
|
|
570
|
+
# Membership
|
|
571
|
+
seg.includes?(a) # => true (vertex check only)
|
|
572
|
+
seg.includes?(seg.midpoint) # => false
|
|
573
|
+
seg.contains?(seg.midpoint) # => true (on-segment check)
|
|
574
|
+
|
|
575
|
+
# Intersection
|
|
576
|
+
seg.intersects?(other_seg) # => true/false
|
|
577
|
+
|
|
578
|
+
# Conversion
|
|
579
|
+
seg.reverse # => Segment with swapped endpoints
|
|
580
|
+
seg.to_path # => two-point Path
|
|
581
|
+
seg.to_a # => [start_point, end_point]
|
|
582
|
+
```
|
|
583
|
+
|
|
545
584
|
### Paths
|
|
546
585
|
|
|
547
586
|
`Path` is a directed, ordered sequence of unique coordinates representing routes, trails, or boundaries.
|
|
@@ -572,7 +611,7 @@ route.closest_points_to(other_path) # path-to-path
|
|
|
572
611
|
sub = route.between(a, b) # extract subpath
|
|
573
612
|
left, right = route.split_at(c) # split at waypoint
|
|
574
613
|
route.at_distance(Distance.km(2)) # interpolate along path
|
|
575
|
-
route.bounds # => Areas::
|
|
614
|
+
route.bounds # => Areas::BoundingBox
|
|
576
615
|
route.to_polygon # close into polygon
|
|
577
616
|
route.intersects?(other_path) # crossing detection
|
|
578
617
|
route.contains?(point) # on-segment check
|
|
@@ -612,6 +651,78 @@ park.distance_to(liberty).to_km # => "12.47 km"
|
|
|
612
651
|
|
|
613
652
|
All three attributes (`label`, `geometry`, `metadata`) are mutable.
|
|
614
653
|
|
|
654
|
+
### Vectors
|
|
655
|
+
|
|
656
|
+
`Vector` pairs a `Distance` (magnitude) with a `Bearing` (direction) to represent a geodetic displacement. It solves the Vincenty direct problem to compute destination points.
|
|
657
|
+
|
|
658
|
+
```ruby
|
|
659
|
+
v = Geodetic::Vector.new(distance: 10_000, bearing: 90.0)
|
|
660
|
+
v = Geodetic::Vector.new(distance: Distance.km(10), bearing: Bearing.new(90))
|
|
661
|
+
|
|
662
|
+
v.north # => north component in meters
|
|
663
|
+
v.east # => east component in meters
|
|
664
|
+
v.magnitude # => distance in meters
|
|
665
|
+
v.reverse # => same distance, opposite bearing
|
|
666
|
+
v.normalize # => unit vector (1 meter)
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
**Vector arithmetic:**
|
|
670
|
+
|
|
671
|
+
```ruby
|
|
672
|
+
v1 + v2 # => Vector (component-wise addition)
|
|
673
|
+
v1 - v2 # => Vector (component-wise subtraction)
|
|
674
|
+
v * 3 # => Vector (scale distance)
|
|
675
|
+
v / 2 # => Vector (scale distance)
|
|
676
|
+
-v # => Vector (reverse bearing)
|
|
677
|
+
v.dot(v2) # => Float (dot product)
|
|
678
|
+
v.cross(v2) # => Float (2D cross product)
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
**Factory methods:**
|
|
682
|
+
|
|
683
|
+
```ruby
|
|
684
|
+
Vector.from_components(north: 1000, east: 500)
|
|
685
|
+
Vector.from_segment(segment)
|
|
686
|
+
segment.to_vector
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Geodetic Arithmetic
|
|
690
|
+
|
|
691
|
+
Operators build geometry from coordinates, vectors, and distances:
|
|
692
|
+
|
|
693
|
+
```ruby
|
|
694
|
+
# Building geometry with +
|
|
695
|
+
p1 + p2 # => Segment
|
|
696
|
+
p1 + p2 + p3 # => Path
|
|
697
|
+
p1 + segment # => Path
|
|
698
|
+
segment + p3 # => Path
|
|
699
|
+
segment + segment # => Path
|
|
700
|
+
p1 + distance # => Circle
|
|
701
|
+
p1 + vector # => Segment (to destination)
|
|
702
|
+
segment + vector # => Path (extend from endpoint)
|
|
703
|
+
vector + segment # => Path (prepend via reverse)
|
|
704
|
+
path + vector # => Path (extend from last point)
|
|
705
|
+
vector + coordinate # => Segment
|
|
706
|
+
distance + coordinate # => Circle
|
|
707
|
+
|
|
708
|
+
# Translation with * or .translate
|
|
709
|
+
p1 * vector # => Coordinate (translated point)
|
|
710
|
+
segment * vector # => Segment (translated endpoints)
|
|
711
|
+
path * vector # => Path (translated waypoints)
|
|
712
|
+
circle * vector # => Circle (translated centroid)
|
|
713
|
+
polygon * vector # => Polygon (translated vertices)
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
### Corridors
|
|
717
|
+
|
|
718
|
+
Convert a path into a polygon corridor of a given width:
|
|
719
|
+
|
|
720
|
+
```ruby
|
|
721
|
+
route = seattle + portland + sf
|
|
722
|
+
corridor = route.to_corridor(width: 1000) # 1km wide polygon
|
|
723
|
+
corridor = route.to_corridor(width: Distance.km(1))
|
|
724
|
+
```
|
|
725
|
+
|
|
615
726
|
### Web Mercator Tile Coordinates
|
|
616
727
|
|
|
617
728
|
```ruby
|
|
@@ -642,6 +753,8 @@ The [`examples/`](examples/) directory contains runnable demo scripts showing pr
|
|
|
642
753
|
| [`04_bearing_calculations.rb`](examples/04_bearing_calculations.rb) | Bearing class, compass directions, elevation angles, and chain bearings |
|
|
643
754
|
| [`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
755
|
| [`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 |
|
|
756
|
+
| [`07_segments_and_shapes.rb`](examples/07_segments_and_shapes.rb) | Segment and polygon subclasses: Triangle, Rectangle, Pentagon, Hexagon, Octagon with containment, edges, and bounding boxes |
|
|
757
|
+
| [`08_geodetic_arithmetic.rb`](examples/08_geodetic_arithmetic.rb) | Geodetic arithmetic: building geometry with + (Segments, Paths, Circles), Vector class (Vincenty direct, components, arithmetic, dot/cross products), translation with * (Coordinates, Segments, Paths, Circles, Polygons), and corridors |
|
|
645
758
|
|
|
646
759
|
Run any example with:
|
|
647
760
|
|
|
@@ -173,11 +173,11 @@ Neighbors preserve the same precision as the original code. Latitude is clamped
|
|
|
173
173
|
|
|
174
174
|
## Area
|
|
175
175
|
|
|
176
|
-
The `to_area` method returns the GARS cell as a `Geodetic::Areas::
|
|
176
|
+
The `to_area` method returns the GARS cell as a `Geodetic::Areas::BoundingBox`.
|
|
177
177
|
|
|
178
178
|
```ruby
|
|
179
179
|
area = coord.to_area
|
|
180
|
-
# => Geodetic::Areas::
|
|
180
|
+
# => Geodetic::Areas::BoundingBox
|
|
181
181
|
|
|
182
182
|
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
|
|
183
183
|
area.nw # => LLA (northwest corner)
|
|
@@ -154,11 +154,11 @@ Neighbors preserve the same precision as the original code. Latitude is clamped
|
|
|
154
154
|
|
|
155
155
|
## Area
|
|
156
156
|
|
|
157
|
-
The `to_area` method returns the GEOREF cell as a `Geodetic::Areas::
|
|
157
|
+
The `to_area` method returns the GEOREF cell as a `Geodetic::Areas::BoundingBox`.
|
|
158
158
|
|
|
159
159
|
```ruby
|
|
160
160
|
area = coord.to_area
|
|
161
|
-
# => Geodetic::Areas::
|
|
161
|
+
# => Geodetic::Areas::BoundingBox
|
|
162
162
|
|
|
163
163
|
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
|
|
164
164
|
area.nw # => LLA (northwest corner)
|
|
@@ -142,11 +142,11 @@ Neighbors preserve the same precision as the original geohash. Longitude wraps c
|
|
|
142
142
|
|
|
143
143
|
## Area
|
|
144
144
|
|
|
145
|
-
The `to_area` method returns the geohash cell as a `Geodetic::Areas::
|
|
145
|
+
The `to_area` method returns the geohash cell as a `Geodetic::Areas::BoundingBox`.
|
|
146
146
|
|
|
147
147
|
```ruby
|
|
148
148
|
area = coord.to_area
|
|
149
|
-
# => Geodetic::Areas::
|
|
149
|
+
# => Geodetic::Areas::BoundingBox
|
|
150
150
|
|
|
151
151
|
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
|
|
152
152
|
area.nw # => LLA (northwest corner)
|
|
@@ -137,11 +137,11 @@ Neighbor computation propagates carries when the adjustment wraps beyond the mat
|
|
|
137
137
|
|
|
138
138
|
## Area
|
|
139
139
|
|
|
140
|
-
The `to_area` method returns the geohash cell as a `Geodetic::Areas::
|
|
140
|
+
The `to_area` method returns the geohash cell as a `Geodetic::Areas::BoundingBox`.
|
|
141
141
|
|
|
142
142
|
```ruby
|
|
143
143
|
area = coord.to_area
|
|
144
|
-
# => Geodetic::Areas::
|
|
144
|
+
# => Geodetic::Areas::BoundingBox
|
|
145
145
|
|
|
146
146
|
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
|
|
147
147
|
area.nw # => LLA (northwest corner)
|
|
@@ -36,8 +36,8 @@ Geodetic searches these paths automatically:
|
|
|
36
36
|
|
|
37
37
|
| Feature | GH/OLC/GARS/GEOREF/HAM | H3 |
|
|
38
38
|
|---------|------------------------|-----|
|
|
39
|
-
| Cell shape |
|
|
40
|
-
| `to_area` returns | `Areas::
|
|
39
|
+
| Cell shape | BoundingBox | Hexagon (6 vertices) |
|
|
40
|
+
| `to_area` returns | `Areas::BoundingBox` | `Areas::Polygon` |
|
|
41
41
|
| `neighbors` returns | Hash with 8 cardinal keys | Array of 6 cells |
|
|
42
42
|
| Code format | String | 64-bit integer (hex string) |
|
|
43
43
|
| Dependency | None (pure Ruby) | `libh3` (C library via fiddle) |
|
|
@@ -150,11 +150,11 @@ Neighbors preserve the same precision as the original locator. Latitude is clamp
|
|
|
150
150
|
|
|
151
151
|
## Area
|
|
152
152
|
|
|
153
|
-
The `to_area` method returns the grid square as a `Geodetic::Areas::
|
|
153
|
+
The `to_area` method returns the grid square as a `Geodetic::Areas::BoundingBox`.
|
|
154
154
|
|
|
155
155
|
```ruby
|
|
156
156
|
area = coord.to_area
|
|
157
|
-
# => Geodetic::Areas::
|
|
157
|
+
# => Geodetic::Areas::BoundingBox
|
|
158
158
|
|
|
159
159
|
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
|
|
160
160
|
area.nw # => LLA (northwest corner)
|
|
@@ -155,11 +155,11 @@ Neighbors preserve the same precision as the original code. Latitude is clamped
|
|
|
155
155
|
|
|
156
156
|
## Area
|
|
157
157
|
|
|
158
|
-
The `to_area` method returns the plus code cell as a `Geodetic::Areas::
|
|
158
|
+
The `to_area` method returns the plus code cell as a `Geodetic::Areas::BoundingBox`.
|
|
159
159
|
|
|
160
160
|
```ruby
|
|
161
161
|
area = coord.to_area
|
|
162
|
-
# => Geodetic::Areas::
|
|
162
|
+
# => Geodetic::Areas::BoundingBox
|
|
163
163
|
|
|
164
164
|
area.includes?(coord.to_lla) # => true (midpoint is inside the cell)
|
|
165
165
|
area.nw # => LLA (northwest corner)
|
data/docs/index.md
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
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
|
-
<li><strong>Geographic Areas</strong> - Circle, Polygon,
|
|
17
|
+
<li><strong>Geographic Areas</strong> - Circle, Polygon, BoundingBox, Triangle, Rectangle, Pentagon, Hexagon, Octagon<br>
|
|
18
|
+
<li><strong>Segments</strong> - Directed two-point line segments with projection, intersection, and interpolation<br>
|
|
18
19
|
<li><strong>Paths</strong> - Directed coordinate sequences with navigation, interpolation, closest approach, intersection, and area conversion<br>
|
|
19
20
|
<li><strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
|
|
20
21
|
<li><strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
@@ -57,7 +58,8 @@ Geodetic supports full bidirectional conversion between all 18 coordinate system
|
|
|
57
58
|
|
|
58
59
|
- **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.
|
|
59
60
|
- **Geoid height calculations** -- Convert between ellipsoidal and orthometric heights using models such as EGM96, EGM2008, GEOID18, and GEOID12B.
|
|
60
|
-
- **Geographic areas** -- `Geodetic::Areas::Circle`, `Geodetic::Areas::Polygon`,
|
|
61
|
+
- **Geographic areas** -- `Geodetic::Areas::Circle`, `Geodetic::Areas::Polygon`, `Geodetic::Areas::BoundingBox`, plus polygon subclasses (`Triangle`, `Rectangle`, `Pentagon`, `Hexagon`, `Octagon`) for point-in-area testing.
|
|
62
|
+
- **Segments** -- `Geodetic::Segment` is a directed two-point line segment with projection, intersection detection, interpolation, and membership testing. It is the geometric primitive underlying Path and Polygon operations.
|
|
61
63
|
- **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
64
|
- **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.
|
|
63
65
|
|
data/docs/reference/areas.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Areas Reference
|
|
2
2
|
|
|
3
|
-
The `Geodetic::Areas` module provides
|
|
3
|
+
The `Geodetic::Areas` module provides geometric area classes for point-in-area testing: `Circle`, `Polygon`, `BoundingBox`, and polygon subclasses (`Triangle`, `Rectangle`, `Pentagon`, `Hexagon`, `Octagon`). All operate on `Geodetic::Coordinate::LLA` points.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -107,18 +107,38 @@ Returns `true` if the given LLA point falls outside the polygon. The logical inv
|
|
|
107
107
|
polygon.excludes?(point) # => true or false
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
+
#### `segments` / `edges` / `border`
|
|
111
|
+
|
|
112
|
+
Returns an array of `Segment` objects for each edge of the polygon.
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
polygon.segments # => [Segment(p1→p2), Segment(p2→p3), Segment(p3→p1)]
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Self-Intersection Validation
|
|
119
|
+
|
|
120
|
+
By default the constructor validates that no edge crosses another edge. Pass `validate: false` to skip this check (used by subclasses that generate known-good geometry).
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
Geodetic::Areas::Polygon.new(boundary: points) # validates
|
|
124
|
+
Geodetic::Areas::Polygon.new(boundary: points, validate: false) # skips
|
|
125
|
+
```
|
|
126
|
+
|
|
110
127
|
### Alias Summary
|
|
111
128
|
|
|
112
129
|
| Primary Method | Aliases |
|
|
113
130
|
|---------------|---------|
|
|
114
131
|
| `includes?` | `include?`, `inside?` |
|
|
115
132
|
| `excludes?` | `exclude?`, `outside?` |
|
|
133
|
+
| `segments` | `edges`, `border` |
|
|
116
134
|
|
|
117
135
|
---
|
|
118
136
|
|
|
119
|
-
## Geodetic::Areas::
|
|
137
|
+
## Geodetic::Areas::BoundingBox
|
|
120
138
|
|
|
121
|
-
Defines an axis-aligned
|
|
139
|
+
Defines an axis-aligned bounding box by its northwest and southeast corners. Edges are always oriented East-West and North-South.
|
|
140
|
+
|
|
141
|
+
> **Note:** `Rectangle` is a separate class (`Polygon` subclass) constructed from a `Segment` centerline. It is not related to `BoundingBox`.
|
|
122
142
|
|
|
123
143
|
### Constructor
|
|
124
144
|
|
|
@@ -126,7 +146,7 @@ Defines an axis-aligned rectangle by its northwest and southeast corners.
|
|
|
126
146
|
nw = Geodetic::Coordinate::LLA.new(lat: 41.0, lng: -75.0)
|
|
127
147
|
se = Geodetic::Coordinate::LLA.new(lat: 40.0, lng: -74.0)
|
|
128
148
|
|
|
129
|
-
|
|
149
|
+
bbox = Geodetic::Areas::BoundingBox.new(nw: nw, se: se)
|
|
130
150
|
```
|
|
131
151
|
|
|
132
152
|
The constructor accepts any coordinate type that responds to `to_lla` -- coordinates are automatically converted to LLA.
|
|
@@ -134,7 +154,7 @@ The constructor accepts any coordinate type that responds to `to_lla` -- coordin
|
|
|
134
154
|
```ruby
|
|
135
155
|
nw_wm = Geodetic::Coordinate::WebMercator.from_lla(nw)
|
|
136
156
|
se_wm = Geodetic::Coordinate::WebMercator.from_lla(se)
|
|
137
|
-
|
|
157
|
+
bbox = Geodetic::Areas::BoundingBox.new(nw: nw_wm, se: se_wm)
|
|
138
158
|
```
|
|
139
159
|
|
|
140
160
|
Raises `ArgumentError` if the NW corner has a lower latitude than the SE corner, or if the NW corner has a higher longitude than the SE corner.
|
|
@@ -152,27 +172,27 @@ All attributes are read-only.
|
|
|
152
172
|
### Computed Corners
|
|
153
173
|
|
|
154
174
|
```ruby
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
bbox.ne # => LLA (nw.lat, se.lng)
|
|
176
|
+
bbox.sw # => LLA (se.lat, nw.lng)
|
|
157
177
|
```
|
|
158
178
|
|
|
159
179
|
### Methods
|
|
160
180
|
|
|
161
181
|
#### `includes?(a_point)` / `include?(a_point)` / `inside?(a_point)`
|
|
162
182
|
|
|
163
|
-
Returns `true` if the given point falls within (or on the boundary of) the
|
|
183
|
+
Returns `true` if the given point falls within (or on the boundary of) the bounding box. Accepts any coordinate type that responds to `to_lla`.
|
|
164
184
|
|
|
165
185
|
```ruby
|
|
166
186
|
point = Geodetic::Coordinate::LLA.new(lat: 40.5, lng: -74.5)
|
|
167
|
-
|
|
187
|
+
bbox.includes?(point) # => true
|
|
168
188
|
```
|
|
169
189
|
|
|
170
190
|
#### `excludes?(a_point)` / `exclude?(a_point)` / `outside?(a_point)`
|
|
171
191
|
|
|
172
|
-
Returns `true` if the given point falls outside the
|
|
192
|
+
Returns `true` if the given point falls outside the bounding box.
|
|
173
193
|
|
|
174
194
|
```ruby
|
|
175
|
-
|
|
195
|
+
bbox.excludes?(point) # => true or false
|
|
176
196
|
```
|
|
177
197
|
|
|
178
198
|
### Alias Summary
|
|
@@ -182,14 +202,120 @@ rectangle.excludes?(point) # => true or false
|
|
|
182
202
|
| `includes?` | `include?`, `inside?` |
|
|
183
203
|
| `excludes?` | `exclude?`, `outside?` |
|
|
184
204
|
|
|
185
|
-
### Integration with
|
|
205
|
+
### Integration with Spatial Hashes
|
|
186
206
|
|
|
187
|
-
`
|
|
207
|
+
Spatial hash coordinate systems (`GH`, `GH36`, `HAM`, `OLC`, `GEOREF`, `GARS`) return a `BoundingBox` from `to_area`:
|
|
188
208
|
|
|
189
209
|
```ruby
|
|
190
210
|
gh36 = Geodetic::Coordinate::GH36.new("bdrdC26BqH")
|
|
191
211
|
area = gh36.to_area
|
|
192
|
-
# => Geodetic::Areas::
|
|
212
|
+
# => Geodetic::Areas::BoundingBox
|
|
193
213
|
|
|
194
214
|
area.includes?(gh36.to_lla) # => true (midpoint is inside the cell)
|
|
195
215
|
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Polygon Subclasses
|
|
220
|
+
|
|
221
|
+
All polygon subclasses inherit from `Polygon` and share its `includes?`/`excludes?`, `segments`, `centroid`, and `boundary` methods. Each adds shape-specific constructors and attributes.
|
|
222
|
+
|
|
223
|
+
### Geodetic::Areas::Triangle
|
|
224
|
+
|
|
225
|
+
A three-sided polygon with four construction modes.
|
|
226
|
+
|
|
227
|
+
#### Constructors
|
|
228
|
+
|
|
229
|
+
```ruby
|
|
230
|
+
center = Geodetic::Coordinate::LLA.new(lat: 40.7484, lng: -73.9857, alt: 0)
|
|
231
|
+
|
|
232
|
+
# Isosceles — width and height from a center point
|
|
233
|
+
tri = Geodetic::Areas::Triangle.new(center: center, width: 400, height: 600, bearing: 0)
|
|
234
|
+
|
|
235
|
+
# Equilateral by circumradius
|
|
236
|
+
tri = Geodetic::Areas::Triangle.new(center: center, radius: 500, bearing: 45)
|
|
237
|
+
|
|
238
|
+
# Equilateral by side length
|
|
239
|
+
tri = Geodetic::Areas::Triangle.new(center: center, side: 600)
|
|
240
|
+
|
|
241
|
+
# Arbitrary 3 vertices
|
|
242
|
+
tri = Geodetic::Areas::Triangle.new(vertices: [p1, p2, p3])
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### Attributes and Methods
|
|
246
|
+
|
|
247
|
+
| Method | Returns | Description |
|
|
248
|
+
|--------|---------|-------------|
|
|
249
|
+
| `sides` | Integer | Always 3 |
|
|
250
|
+
| `vertices` | Array<LLA> | The three vertices |
|
|
251
|
+
| `center` | LLA | Center point (or centroid for arbitrary vertices) |
|
|
252
|
+
| `width` | Float | Base width in meters (0 for arbitrary vertices) |
|
|
253
|
+
| `height` | Float | Height in meters (0 for arbitrary vertices) |
|
|
254
|
+
| `bearing` | Float | Bearing in degrees (0 for arbitrary vertices) |
|
|
255
|
+
| `base` | Float/nil | Same as width; nil for arbitrary vertices |
|
|
256
|
+
| `side_lengths` | Array<Float> | Three side lengths in meters |
|
|
257
|
+
| `equilateral?` | Boolean | All sides equal (within 5m tolerance) |
|
|
258
|
+
| `isosceles?` | Boolean | Exactly two sides equal |
|
|
259
|
+
| `scalene?` | Boolean | No two sides equal |
|
|
260
|
+
| `to_bounding_box` | BoundingBox | Axis-aligned bounding box enclosing the triangle |
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
### Geodetic::Areas::Rectangle
|
|
265
|
+
|
|
266
|
+
A four-sided polygon defined by a centerline `Segment` and a perpendicular width. The centerline is the fundamental representation — center, height, and bearing are all derived from it.
|
|
267
|
+
|
|
268
|
+
#### Constructor
|
|
269
|
+
|
|
270
|
+
```ruby
|
|
271
|
+
a = Geodetic::Coordinate::LLA.new(lat: 40.7400, lng: -73.9900, alt: 0)
|
|
272
|
+
b = Geodetic::Coordinate::LLA.new(lat: 40.7500, lng: -73.9900, alt: 0)
|
|
273
|
+
|
|
274
|
+
# From a Segment object
|
|
275
|
+
seg = Geodetic::Segment.new(a, b)
|
|
276
|
+
rect = Geodetic::Areas::Rectangle.new(segment: seg, width: 200)
|
|
277
|
+
|
|
278
|
+
# From an array of two coordinates
|
|
279
|
+
rect = Geodetic::Areas::Rectangle.new(segment: [a, b], width: 200)
|
|
280
|
+
|
|
281
|
+
# Width accepts a Distance instance (converted to meters)
|
|
282
|
+
rect = Geodetic::Areas::Rectangle.new(segment: seg, width: seg.distance)
|
|
283
|
+
rect.square? #=> true
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The `segment:` parameter defines the centerline of the rectangle. Height equals the segment length, bearing equals the segment direction, and center equals the segment midpoint. Width is the perpendicular extent, specified in meters or as a `Distance` instance.
|
|
287
|
+
|
|
288
|
+
#### Attributes and Methods
|
|
289
|
+
|
|
290
|
+
| Method | Returns | Description |
|
|
291
|
+
|--------|---------|-------------|
|
|
292
|
+
| `sides` | Integer | Always 4 |
|
|
293
|
+
| `centerline` | Segment | The centerline segment |
|
|
294
|
+
| `width` | Float | Perpendicular width in meters |
|
|
295
|
+
| `center` | LLA | Midpoint of the centerline |
|
|
296
|
+
| `height` | Float | Length of the centerline in meters |
|
|
297
|
+
| `bearing` | Float | Direction of the centerline in degrees |
|
|
298
|
+
| `corners` | Array<LLA> | Four corners: front-left, front-right, back-right, back-left |
|
|
299
|
+
| `square?` | Boolean | True when width equals height |
|
|
300
|
+
| `to_bounding_box` | BoundingBox | Axis-aligned bounding box enclosing the rectangle |
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
### Geodetic::Areas::Pentagon / Hexagon / Octagon
|
|
305
|
+
|
|
306
|
+
Regular polygons constructed from a center point and circumradius.
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
center = Geodetic::Coordinate::LLA.new(lat: 40.7484, lng: -73.9857, alt: 0)
|
|
310
|
+
|
|
311
|
+
pent = Geodetic::Areas::Pentagon.new(center: center, radius: 500, bearing: 0)
|
|
312
|
+
hex = Geodetic::Areas::Hexagon.new(center: center, radius: 500, bearing: 0)
|
|
313
|
+
oct = Geodetic::Areas::Octagon.new(center: center, radius: 500, bearing: 0)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
| Method | Returns | Description |
|
|
317
|
+
|--------|---------|-------------|
|
|
318
|
+
| `sides` | Integer | 5, 6, or 8 respectively |
|
|
319
|
+
| `center` | LLA | Center point |
|
|
320
|
+
| `radius` | Float | Circumradius in meters |
|
|
321
|
+
| `bearing` | Float | Rotation bearing in degrees |
|