geodetic 0.5.2 → 0.6.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 +39 -0
- data/README.md +71 -0
- data/docs/index.md +4 -0
- data/docs/reference/wkb.md +385 -0
- data/docs/reference/wkt.md +354 -0
- data/examples/10_wkt_serialization.rb +248 -0
- data/examples/11_wkb_serialization.rb +261 -0
- data/examples/README.md +30 -0
- data/examples/geodetic_demo.wkb +0 -0
- data/examples/geodetic_demo.wkt +5 -0
- data/examples/geodetic_demo_output.wkb.hex +5 -0
- data/examples/sample_geometries.wkb +0 -0
- data/examples/sample_geometries.wkb.hex +60 -0
- data/lib/geodetic/version.rb +1 -1
- data/lib/geodetic/wkb.rb +360 -0
- data/lib/geodetic/wkt.rb +313 -0
- data/lib/geodetic.rb +2 -0
- data/mkdocs.yml +2 -0
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e34b06e1bf493a580a70871173cab9e79b442c1667a1d68e9b9b2cd6181b660
|
|
4
|
+
data.tar.gz: 3d7cdabbff9c142ae61812dfec12d92cf764cc85cf31458c41226d4f00cc19a2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a8c06b5625fcbb2df864e913ec687f0075c971ff91910a295bfe112ab645bca2174b7d01e32cb9ef03aa9cad05311cfdee7c00572f629d9c4a5b26af70604d3e
|
|
7
|
+
data.tar.gz: 3a9e274d50a491b2b1b1791402e4e5ff5c52f2be2cf172162fadc8d246ef64cf8bc410bb7eb869b1c3d1748cbaa33eb4d7e7cddaf503f6a191fcac09b9c93024
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
## [0.6.0] - 2026-03-10
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`Geodetic::WKT` module** — Well-Known Text serialization for all geometry types
|
|
16
|
+
- **`to_wkt(precision: 6, srid: nil)`** instance method on all 18 coordinate classes, Segment, Path, Areas::Polygon (and subclasses), Areas::Circle, Areas::BoundingBox, and Feature
|
|
17
|
+
- **Coordinate order**: longitude latitude (OGC Simple Features standard)
|
|
18
|
+
- **Z suffix**: automatically added when any point in the geometry has non-zero altitude; Z-dimensionality is uniform within each geometry
|
|
19
|
+
- **EWKT**: `srid:` option prepends `SRID=N;` for PostGIS compatibility
|
|
20
|
+
- **`WKT.parse(string)`** — parse WKT/EWKT into Geodetic objects (Point → LLA, LineString → Segment/Path, Polygon → Areas::Polygon, Multi*/GeometryCollection → Array)
|
|
21
|
+
- **`WKT.parse_with_srid(string)`** — returns `[object, srid]` tuple
|
|
22
|
+
- **`WKT.save!(path, *objects, srid:, precision:)`** — write one WKT per line
|
|
23
|
+
- **`WKT.load(path)`** — read WKT file into Array of Geodetic objects
|
|
24
|
+
- ENU/NED raise `ArgumentError` (relative systems cannot be exported)
|
|
25
|
+
- **`Geodetic::WKB` module** — Well-Known Binary serialization for all geometry types
|
|
26
|
+
- **`to_wkb(srid: nil)`** and **`to_wkb_hex(srid: nil)`** instance methods on all 18 coordinate classes, Segment, Path, Areas::Polygon (and subclasses), Areas::Circle, Areas::BoundingBox, and Feature
|
|
27
|
+
- **Byte order**: output is always little-endian (NDR), matching PostGIS, GEOS, RGeo, and Shapely; parser supports both LE and BE input
|
|
28
|
+
- **ISO WKB Z**: type code + 1000 (Point Z = 1001, LineString Z = 1002, Polygon Z = 1003) when any point has non-zero altitude
|
|
29
|
+
- **EWKB**: `srid:` option embeds SRID via `0x20000000` flag for PostGIS compatibility
|
|
30
|
+
- **`WKB.parse(input)`** — parse WKB from binary or hex string (auto-detects encoding)
|
|
31
|
+
- **`WKB.parse_with_srid(input)`** — returns `[object, srid]` tuple
|
|
32
|
+
- **Binary file I/O**: `WKB.save!(path, *objects, srid:)` / `WKB.load(path)` — framed format (4-byte LE count + size-prefixed WKB)
|
|
33
|
+
- **Hex file I/O**: `WKB.save_hex!(path, *objects, srid:)` / `WKB.load_hex(path)` — one hex string per line, supports `#` comments
|
|
34
|
+
- Supports all WKB types: Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection
|
|
35
|
+
- ENU/NED raise `ArgumentError` (relative systems cannot be exported)
|
|
36
|
+
- WKT example (`examples/10_wkt_serialization.rb`) — 10-section demo covering export, SRID/EWKT, Z-dimension, parsing, roundtrip, and file I/O
|
|
37
|
+
- WKB example (`examples/11_wkb_serialization.rb`) — 10-section demo covering export, EWKB/SRID, Z-dimension, parsing, roundtrip, and binary/hex file I/O
|
|
38
|
+
- WKB fixture files: `examples/sample_geometries.wkb` (9 geometries) and `examples/sample_geometries.wkb.hex` (15 geometries with comments)
|
|
39
|
+
- 53 WKT tests (134 assertions) and 54 WKB tests (144 assertions)
|
|
40
|
+
- Documentation: `docs/reference/wkt.md` and `docs/reference/wkb.md`
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Updated README with WKT and WKB sections, key features, and examples 10-11 in the examples table
|
|
45
|
+
- Updated `docs/index.md` with WKT and WKB in key features and reference links
|
|
46
|
+
- Updated `examples/README.md` with example 10 and 11 descriptions
|
|
47
|
+
- Updated `mkdocs.yml` nav with WKT and WKB reference pages
|
|
48
|
+
- Added `require_relative "geodetic/wkt"` and `require_relative "geodetic/wkb"` to `lib/geodetic.rb`
|
|
49
|
+
|
|
11
50
|
## [0.5.2] - 2026-03-10
|
|
12
51
|
|
|
13
52
|
### Added
|
data/README.md
CHANGED
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
- <strong>Vectors</strong> - Geodetic displacement (distance + bearing) with full arithmetic and Vincenty direct<br>
|
|
26
26
|
- <strong>Geodetic Arithmetic</strong> - Compose geometry with operators: P1 + P2 → Segment, + P3 → Path, + Distance → Circle, * Vector → translate<br>
|
|
27
27
|
- <strong>GeoJSON Export</strong> - Build FeatureCollections from any mix of objects and save to file<br>
|
|
28
|
+
- <strong>WKT Serialization</strong> - Well-Known Text export/import with SRID/EWKT and Z-dimension support<br>
|
|
29
|
+
- <strong>WKB Serialization</strong> - Well-Known Binary export/import with EWKB, SRID, hex encoding, and file I/O<br>
|
|
28
30
|
- <strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
29
31
|
- <strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
|
|
30
32
|
- <strong>Multiple Datums</strong> - WGS84, Clarke 1866, GRS 1980, Airy 1830, and more<br>
|
|
@@ -769,6 +771,73 @@ objects = Geodetic::GeoJSON.load("map.geojson")
|
|
|
769
771
|
|
|
770
772
|
`load` returns an Array of Geodetic objects. Features with a `"name"` or non-empty properties round-trip as `Feature` objects; bare geometries with empty properties return as raw coordinates, segments, paths, or polygons. `GeoJSON.parse(hash)` does the same from an already-parsed Hash.
|
|
771
773
|
|
|
774
|
+
### WKT Serialization
|
|
775
|
+
|
|
776
|
+
`WKT` provides Well-Known Text export and import for all geometry types. WKT is the standard format used by PostGIS, RGeo, Shapely, JTS, and most GIS tools.
|
|
777
|
+
|
|
778
|
+
```ruby
|
|
779
|
+
seattle.to_wkt # => "POINT(-122.3493 47.6205)"
|
|
780
|
+
seattle.to_wkt(srid: 4326) # => "SRID=4326;POINT(-122.3493 47.6205)"
|
|
781
|
+
seattle.to_wkt(precision: 2) # => "POINT(-122.35 47.62)"
|
|
782
|
+
|
|
783
|
+
Segment.new(seattle, portland).to_wkt # => "LINESTRING(-122.3493 47.6205, -122.6784 45.5152)"
|
|
784
|
+
route.to_wkt # => "LINESTRING(...)"
|
|
785
|
+
polygon.to_wkt # => "POLYGON((...))
|
|
786
|
+
circle.to_wkt(segments: 64) # => "POLYGON((...)) (64-gon)"
|
|
787
|
+
feature.to_wkt # => delegates to geometry (WKT has no properties)
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
Altitude triggers the Z suffix. When any point has altitude, all points in that geometry get Z:
|
|
791
|
+
|
|
792
|
+
```ruby
|
|
793
|
+
Geodetic::Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 184.0).to_wkt
|
|
794
|
+
# => "POINT Z(-122.35 47.62 184.0)"
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
**File I/O and parsing:**
|
|
798
|
+
|
|
799
|
+
```ruby
|
|
800
|
+
# Save to file (one WKT per line)
|
|
801
|
+
Geodetic::WKT.save!("shapes.wkt", seattle, segment, polygon, srid: 4326)
|
|
802
|
+
|
|
803
|
+
# Load from file
|
|
804
|
+
objects = Geodetic::WKT.load("shapes.wkt")
|
|
805
|
+
# => [Coordinate::LLA, Segment, Areas::Polygon]
|
|
806
|
+
|
|
807
|
+
# Parse a single WKT string
|
|
808
|
+
obj = Geodetic::WKT.parse("POINT(-122.3493 47.6205)")
|
|
809
|
+
obj, srid = Geodetic::WKT.parse_with_srid("SRID=4326;POLYGON((-122 47, -121 46, -123 46, -122 47))")
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### WKB Serialization
|
|
813
|
+
|
|
814
|
+
`WKB` provides Well-Known Binary export and import — the binary counterpart to WKT used by PostGIS, GEOS, RGeo, and Shapely for efficient geometry storage. Output is always little-endian (NDR).
|
|
815
|
+
|
|
816
|
+
```ruby
|
|
817
|
+
seattle.to_wkb # => 21-byte binary string
|
|
818
|
+
seattle.to_wkb_hex # => "01010000008a1f63ee5a965ec0..."
|
|
819
|
+
seattle.to_wkb_hex(srid: 4326) # => EWKB with embedded SRID
|
|
820
|
+
|
|
821
|
+
Segment.new(seattle, portland).to_wkb_hex # => LINESTRING hex
|
|
822
|
+
polygon.to_wkb_hex # => POLYGON hex
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
**File I/O (binary and hex):**
|
|
826
|
+
|
|
827
|
+
```ruby
|
|
828
|
+
# Binary format (framed: count + size-prefixed WKB)
|
|
829
|
+
Geodetic::WKB.save!("shapes.wkb", seattle, segment, polygon)
|
|
830
|
+
objects = Geodetic::WKB.load("shapes.wkb")
|
|
831
|
+
|
|
832
|
+
# Hex format (one hex string per line, supports comments)
|
|
833
|
+
Geodetic::WKB.save_hex!("shapes.wkb.hex", seattle, segment, polygon)
|
|
834
|
+
objects = Geodetic::WKB.load_hex("shapes.wkb.hex")
|
|
835
|
+
|
|
836
|
+
# Parse a single hex or binary string
|
|
837
|
+
obj = Geodetic::WKB.parse("01010000008a1f63ee5a965ec08195438b6ccf4740")
|
|
838
|
+
obj, srid = Geodetic::WKB.parse_with_srid(ewkb_hex)
|
|
839
|
+
```
|
|
840
|
+
|
|
772
841
|
### Web Mercator Tile Coordinates
|
|
773
842
|
|
|
774
843
|
```ruby
|
|
@@ -802,6 +871,8 @@ The [`examples/`](examples/) directory contains runnable demo scripts showing pr
|
|
|
802
871
|
| [`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 |
|
|
803
872
|
| [`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 |
|
|
804
873
|
| [`09_geojson_export.rb`](examples/09_geojson_export.rb) | GeoJSON export: `to_geojson` on all geometry types, `GeoJSON` class for building FeatureCollections with `<<`, delete/clear, Enumerable, and `save` to file |
|
|
874
|
+
| [`10_wkt_serialization.rb`](examples/10_wkt_serialization.rb) | WKT serialization: `to_wkt` on all geometry types, SRID/EWKT, Z-dimension handling, parsing, and roundtrip verification |
|
|
875
|
+
| [`11_wkb_serialization.rb`](examples/11_wkb_serialization.rb) | WKB serialization: `to_wkb`/`to_wkb_hex` on all geometry types, EWKB/SRID, Z-dimension, parsing, roundtrip, and binary/hex file I/O |
|
|
805
876
|
|
|
806
877
|
Run any example with:
|
|
807
878
|
|
data/docs/index.md
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
<li><strong>Paths</strong> - Directed coordinate sequences with navigation, interpolation, closest approach, intersection, and area conversion<br>
|
|
20
20
|
<li><strong>Features</strong> - Named geometry wrapper with metadata and delegated distance/bearing<br>
|
|
21
21
|
<li><strong>GeoJSON Export</strong> - Build FeatureCollections from any mix of objects and save to file<br>
|
|
22
|
+
<li><strong>WKT Serialization</strong> - Well-Known Text export/import with SRID/EWKT and Z-dimension support<br>
|
|
23
|
+
<li><strong>WKB Serialization</strong> - Well-Known Binary export/import with EWKB, SRID, hex encoding, and file I/O<br>
|
|
22
24
|
<li><strong>Validated Setters</strong> - Type coercion and range validation on all coordinate attributes<br>
|
|
23
25
|
<li><strong>Serialization</strong> - to_s(precision), to_a, from_string, from_array, DMS format<br>
|
|
24
26
|
<li><strong>Multiple Datums</strong> - WGS84, Clarke 1866, GRS 1980, Airy 1830, and more<br>
|
|
@@ -114,4 +116,6 @@ puts lla_again.to_s
|
|
|
114
116
|
- [Vector](reference/vector.md)
|
|
115
117
|
- [Arithmetic](reference/arithmetic.md)
|
|
116
118
|
- [GeoJSON Export](reference/geojson.md)
|
|
119
|
+
- [WKT Serialization](reference/wkt.md)
|
|
120
|
+
- [WKB Serialization](reference/wkb.md)
|
|
117
121
|
- [Map Rendering](reference/map-rendering.md)
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# WKB Serialization Reference
|
|
2
|
+
|
|
3
|
+
`Geodetic::WKB` provides [Well-Known Binary](https://en.wikipedia.org/wiki/Well-known_binary_representation_of_geometry) (WKB) serialization for all Geodetic geometry types. WKB is the binary counterpart to WKT, used by PostGIS, GEOS, RGeo, Shapely, and most GIS tools for efficient geometry storage and transfer.
|
|
4
|
+
|
|
5
|
+
Every geometry type gains `to_wkb` and `to_wkb_hex` instance methods. The module also provides `WKB.parse` and `WKB.parse_with_srid` for importing WKB data back into Geodetic objects.
|
|
6
|
+
|
|
7
|
+
**Byte order:** Output is always little-endian (NDR), matching PostGIS, GEOS, RGeo, Shapely, and virtually all modern GIS tools. Big-endian (XDR) input is fully supported by the parser.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Export
|
|
12
|
+
|
|
13
|
+
### Coordinates → POINT
|
|
14
|
+
|
|
15
|
+
All 18 coordinate classes gain `to_wkb` and `to_wkb_hex` methods. They convert the coordinate to LLA and return WKB binary or hex-encoded output.
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
|
|
19
|
+
|
|
20
|
+
seattle.to_wkb # => 21-byte binary string
|
|
21
|
+
seattle.to_wkb_hex # => "01010000008a1f63ee5a965ec08195438b6ccf4740"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Altitude handling:** When altitude is non-zero, the Z type code is used (Point Z = 1001):
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
Geodetic::Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 184.0).to_wkb_hex
|
|
28
|
+
# => "01e90300008a1f63ee5a965ec08195438b6ccf47400000000000006740"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Cross-system:** Any coordinate type works — UTM, ECEF, MGRS, etc. are converted to LLA internally.
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
seattle.to_utm.to_wkb_hex # => hex string
|
|
35
|
+
seattle.to_ecef.to_wkb_hex # => hex string
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**ENU and NED:** These are relative coordinate systems. Calling `to_wkb` raises `ArgumentError`.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
### Segment → LINESTRING
|
|
43
|
+
|
|
44
|
+
```ruby
|
|
45
|
+
seg = Geodetic::Segment.new(seattle, portland)
|
|
46
|
+
seg.to_wkb # => 41-byte binary string
|
|
47
|
+
seg.to_wkb_hex # => hex string
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### Path → LINESTRING / POLYGON
|
|
53
|
+
|
|
54
|
+
Returns a LINESTRING by default. Pass `as: :polygon` for a closed POLYGON.
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
route = Geodetic::Path.new(coordinates: [seattle, portland, sf])
|
|
58
|
+
route.to_wkb # => LINESTRING binary
|
|
59
|
+
route.to_wkb(as: :polygon) # => POLYGON binary
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
### Areas::Polygon → POLYGON
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
poly = Geodetic::Areas::Polygon.new(boundary: [a, b, c])
|
|
68
|
+
poly.to_wkb # => binary
|
|
69
|
+
poly.to_wkb_hex # => hex string
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
All Polygon subclasses (`Triangle`, `Rectangle`, `Pentagon`, `Hexagon`, `Octagon`) inherit this method.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### Areas::Circle → POLYGON
|
|
77
|
+
|
|
78
|
+
Approximated as a regular N-gon. Default is 32 segments.
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
circle = Geodetic::Areas::Circle.new(centroid: seattle, radius: 10_000)
|
|
82
|
+
|
|
83
|
+
circle.to_wkb # => 32-gon binary (541 bytes)
|
|
84
|
+
circle.to_wkb(segments: 64) # => 64-gon
|
|
85
|
+
circle.to_wkb(segments: 8) # => 8-gon (157 bytes)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### Areas::BoundingBox → POLYGON
|
|
91
|
+
|
|
92
|
+
5 positions (4 corners + closing point).
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
bbox = Geodetic::Areas::BoundingBox.new(
|
|
96
|
+
nw: Geodetic::Coordinate::LLA.new(lat: 48.0, lng: -123.0, alt: 0),
|
|
97
|
+
se: Geodetic::Coordinate::LLA.new(lat: 46.0, lng: -121.0, alt: 0)
|
|
98
|
+
)
|
|
99
|
+
bbox.to_wkb # => 93-byte binary
|
|
100
|
+
bbox.to_wkb_hex # => hex string
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Feature → delegates to geometry
|
|
106
|
+
|
|
107
|
+
WKB has no concept of properties or labels. `Feature#to_wkb` delegates directly to the underlying geometry.
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
f = Geodetic::Feature.new(label: "Seattle", geometry: seattle, metadata: { pop: 750_000 })
|
|
111
|
+
f.to_wkb_hex # => same as seattle.to_wkb_hex
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Options
|
|
117
|
+
|
|
118
|
+
All `to_wkb` and `to_wkb_hex` methods accept:
|
|
119
|
+
|
|
120
|
+
| Parameter | Default | Description |
|
|
121
|
+
|-----------|---------|-------------|
|
|
122
|
+
| `srid:` | `nil` | When set, produces EWKB with embedded SRID |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Z-Dimension Consistency
|
|
127
|
+
|
|
128
|
+
When any point in a geometry has non-zero altitude, **all** points in that geometry use the Z type code. This follows the OGC rule that Z-dimensionality is uniform within a geometry.
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
# seattle (alt=0) + sf (alt=100) → LineString Z (type code 1002)
|
|
132
|
+
mixed = Geodetic::Segment.new(seattle, sf_with_altitude)
|
|
133
|
+
mixed.to_wkb # type code = 1002 (LineString Z)
|
|
134
|
+
|
|
135
|
+
# Both at alt=0 → LineString (type code 2)
|
|
136
|
+
flat = Geodetic::Segment.new(seattle, portland)
|
|
137
|
+
flat.to_wkb # type code = 2 (LineString)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## SRID / EWKB
|
|
143
|
+
|
|
144
|
+
Pass `srid:` to any `to_wkb` or `to_wkb_hex` call to produce Extended WKB (EWKB), the format used by PostGIS:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
seattle.to_wkb_hex(srid: 4326)
|
|
148
|
+
# => "0101000020e61000008a1f63ee5a965ec08195438b6ccf4740"
|
|
149
|
+
|
|
150
|
+
seg.to_wkb_hex(srid: 4326)
|
|
151
|
+
# => "0102000020e6100000020000008a1f63ee..."
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
EWKB embeds the SRID in the type code using the `0x20000000` flag, followed by a 4-byte SRID value.
|
|
155
|
+
|
|
156
|
+
Common SRIDs:
|
|
157
|
+
|
|
158
|
+
| SRID | Description |
|
|
159
|
+
|------|-------------|
|
|
160
|
+
| 4326 | WGS84 geographic (lat/lng) |
|
|
161
|
+
| 3857 | Web Mercator |
|
|
162
|
+
| 32610 | UTM Zone 10N |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## File I/O
|
|
167
|
+
|
|
168
|
+
### Binary Format
|
|
169
|
+
|
|
170
|
+
#### `WKB.save!(path, *objects, srid: nil)`
|
|
171
|
+
|
|
172
|
+
Write geometries to a binary file using a framed format: 4-byte LE count, then for each geometry a 4-byte LE size followed by raw WKB bytes. Accepts individual objects or an array.
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
Geodetic::WKB.save!("shapes.wkb", seattle, segment, polygon)
|
|
176
|
+
Geodetic::WKB.save!("shapes.wkb", [seattle, segment, polygon])
|
|
177
|
+
Geodetic::WKB.save!("shapes.wkb", seattle, polygon, srid: 4326)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### `WKB.load(path)`
|
|
181
|
+
|
|
182
|
+
Read a binary WKB file and return an Array of Geodetic objects.
|
|
183
|
+
|
|
184
|
+
```ruby
|
|
185
|
+
objects = Geodetic::WKB.load("shapes.wkb")
|
|
186
|
+
# => [Coordinate::LLA, Segment, Areas::Polygon]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Hex Format
|
|
190
|
+
|
|
191
|
+
#### `WKB.save_hex!(path, *objects, srid: nil)`
|
|
192
|
+
|
|
193
|
+
Write one hex-encoded WKB string per line. Human-readable and diff-friendly.
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
Geodetic::WKB.save_hex!("shapes.wkb.hex", seattle, segment, polygon)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Output file:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
01010000008a1f63ee5a965ec08195438b6ccf4740
|
|
203
|
+
0102000000020000008a1f63ee5a965ec0...
|
|
204
|
+
01030000000100000004000000...
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### `WKB.load_hex(path)`
|
|
208
|
+
|
|
209
|
+
Read a hex WKB file and return an Array of Geodetic objects. Blank lines and lines starting with `#` are skipped.
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
objects = Geodetic::WKB.load_hex("shapes.wkb.hex")
|
|
213
|
+
# => [Coordinate::LLA, Segment, Areas::Polygon]
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Roundtrip
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
objects = [seattle, segment, polygon]
|
|
220
|
+
|
|
221
|
+
# Binary roundtrip
|
|
222
|
+
Geodetic::WKB.save!("data.wkb", objects)
|
|
223
|
+
loaded = Geodetic::WKB.load("data.wkb")
|
|
224
|
+
|
|
225
|
+
# Hex roundtrip
|
|
226
|
+
Geodetic::WKB.save_hex!("data.wkb.hex", objects, srid: 4326)
|
|
227
|
+
loaded = Geodetic::WKB.load_hex("data.wkb.hex")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Import
|
|
233
|
+
|
|
234
|
+
### `WKB.parse(input)`
|
|
235
|
+
|
|
236
|
+
Parse a WKB binary string or hex string and return a Geodetic object (or Array for Multi*/GeometryCollection types). Auto-detects binary vs hex encoding.
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
Geodetic::WKB.parse("01010000008a1f63ee5a965ec08195438b6ccf4740")
|
|
240
|
+
# => Coordinate::LLA
|
|
241
|
+
|
|
242
|
+
Geodetic::WKB.parse(binary_string)
|
|
243
|
+
# => Coordinate::LLA
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### `WKB.parse_with_srid(input)`
|
|
247
|
+
|
|
248
|
+
Parse WKB/EWKB and return both the object and the SRID:
|
|
249
|
+
|
|
250
|
+
```ruby
|
|
251
|
+
obj, srid = Geodetic::WKB.parse_with_srid("0101000020e61000008a1f63ee5a965ec08195438b6ccf4740")
|
|
252
|
+
obj # => Coordinate::LLA
|
|
253
|
+
srid # => 4326
|
|
254
|
+
|
|
255
|
+
obj, srid = Geodetic::WKB.parse_with_srid(plain_wkb_hex)
|
|
256
|
+
srid # => nil
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## WKB → Geodetic Type Mapping
|
|
262
|
+
|
|
263
|
+
| WKB Type Code | WKB Type | Geodetic Type |
|
|
264
|
+
|---------------|----------|---------------|
|
|
265
|
+
| 1 | POINT | `Coordinate::LLA` |
|
|
266
|
+
| 2 (2 points) | LINESTRING | `Segment` |
|
|
267
|
+
| 2 (3+ points) | LINESTRING | `Path` |
|
|
268
|
+
| 3 | POLYGON | `Areas::Polygon` (outer ring only; holes are dropped) |
|
|
269
|
+
| 4 | MULTIPOINT | Array of `Coordinate::LLA` |
|
|
270
|
+
| 5 | MULTILINESTRING | Array of `Segment` or `Path` |
|
|
271
|
+
| 6 | MULTIPOLYGON | Array of `Areas::Polygon` |
|
|
272
|
+
| 7 | GEOMETRYCOLLECTION | Array of mixed types |
|
|
273
|
+
|
|
274
|
+
Z variants (type + 1000) are supported: Point Z = 1001, LineString Z = 1002, Polygon Z = 1003.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Geometry Mapping (Export)
|
|
279
|
+
|
|
280
|
+
| Geodetic Type | WKB Type | Notes |
|
|
281
|
+
|---------------|----------|-------|
|
|
282
|
+
| Any coordinate (LLA, UTM, ECEF, ...) | POINT | Converts through LLA |
|
|
283
|
+
| `Segment` | LINESTRING | 2 positions |
|
|
284
|
+
| `Path` | LINESTRING | N positions (default) |
|
|
285
|
+
| `Path` (with `as: :polygon`) | POLYGON | Auto-closes the ring |
|
|
286
|
+
| `Areas::Polygon` (and subclasses) | POLYGON | Boundary ring already closed |
|
|
287
|
+
| `Areas::Circle` | POLYGON | Approximated as N-gon (default 32) |
|
|
288
|
+
| `Areas::BoundingBox` | POLYGON | 4 corners, closed |
|
|
289
|
+
| `Feature` | Delegates to geometry | Properties are lost |
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## WKB Binary Structure
|
|
294
|
+
|
|
295
|
+
Each WKB geometry starts with:
|
|
296
|
+
|
|
297
|
+
| Offset | Size | Description |
|
|
298
|
+
|--------|------|-------------|
|
|
299
|
+
| 0 | 1 byte | Byte order: `0x01` = little-endian (NDR), `0x00` = big-endian (XDR) |
|
|
300
|
+
| 1 | 4 bytes | Type code (uint32) |
|
|
301
|
+
| 5 | 4 bytes | SRID (uint32, only in EWKB when SRID flag is set) |
|
|
302
|
+
| 5 or 9 | varies | Geometry data (IEEE 754 doubles) |
|
|
303
|
+
|
|
304
|
+
**Type codes:**
|
|
305
|
+
|
|
306
|
+
| Code | Type | Z Code |
|
|
307
|
+
|------|------|--------|
|
|
308
|
+
| 1 | Point | 1001 |
|
|
309
|
+
| 2 | LineString | 1002 |
|
|
310
|
+
| 3 | Polygon | 1003 |
|
|
311
|
+
| 4 | MultiPoint | 1004 |
|
|
312
|
+
| 5 | MultiLineString | 1005 |
|
|
313
|
+
| 6 | MultiPolygon | 1006 |
|
|
314
|
+
| 7 | GeometryCollection | 1007 |
|
|
315
|
+
|
|
316
|
+
**EWKB flags** (OR'd into type code):
|
|
317
|
+
|
|
318
|
+
| Flag | Value | Description |
|
|
319
|
+
|------|-------|-------------|
|
|
320
|
+
| SRID | `0x20000000` | SRID follows the type code |
|
|
321
|
+
| Z | `0x80000000` | Coordinates include Z values |
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Roundtrip Example
|
|
326
|
+
|
|
327
|
+
```ruby
|
|
328
|
+
require "geodetic"
|
|
329
|
+
|
|
330
|
+
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0)
|
|
331
|
+
|
|
332
|
+
# Export
|
|
333
|
+
hex = seattle.to_wkb_hex(srid: 4326)
|
|
334
|
+
# => "0101000020e61000008a1f63ee5a965ec08195438b6ccf4740"
|
|
335
|
+
|
|
336
|
+
# Import
|
|
337
|
+
obj, srid = Geodetic::WKB.parse_with_srid(hex)
|
|
338
|
+
obj.lat # => 47.6205
|
|
339
|
+
obj.lng # => -122.3493
|
|
340
|
+
srid # => 4326
|
|
341
|
+
|
|
342
|
+
# Re-export
|
|
343
|
+
obj.to_wkb_hex(srid: srid) == hex # => true
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Integration with PostGIS and RGeo
|
|
349
|
+
|
|
350
|
+
WKB is the preferred format for exchanging geometry with spatial databases:
|
|
351
|
+
|
|
352
|
+
```ruby
|
|
353
|
+
# Writing to PostGIS (hex format)
|
|
354
|
+
hex = polygon.to_wkb_hex(srid: 4326)
|
|
355
|
+
ActiveRecord::Base.connection.execute(
|
|
356
|
+
"INSERT INTO regions (geom) VALUES (ST_GeomFromWKB(decode('#{hex}', 'hex'), 4326))"
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
# Or using EWKB directly
|
|
360
|
+
ewkb_hex = polygon.to_wkb_hex(srid: 4326)
|
|
361
|
+
ActiveRecord::Base.connection.execute(
|
|
362
|
+
"INSERT INTO regions (geom) VALUES ('#{ewkb_hex}')"
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Reading from PostGIS
|
|
366
|
+
row = ActiveRecord::Base.connection.select_one("SELECT ST_AsEWKB(geom)::text FROM regions LIMIT 1")
|
|
367
|
+
obj, srid = Geodetic::WKB.parse_with_srid(row["st_asewkb"])
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
WKB binary is also accepted by RGeo's `WKRep::WKBParser`, Shapely's `wkb.loads()`, JTS, GEOS, and virtually every GIS library.
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## WKB vs WKT
|
|
375
|
+
|
|
376
|
+
| | WKB | WKT |
|
|
377
|
+
|---|-----|-----|
|
|
378
|
+
| **Format** | Binary | Text |
|
|
379
|
+
| **Size** | Compact | Larger |
|
|
380
|
+
| **Human-readable** | No (use hex) | Yes |
|
|
381
|
+
| **Speed** | Faster to parse | Slower |
|
|
382
|
+
| **Use case** | Database storage, wire transfer | Debugging, config files, SQL |
|
|
383
|
+
| **SRID support** | EWKB flag | EWKT prefix |
|
|
384
|
+
|
|
385
|
+
Both formats support the same geometry types and Z-dimension handling.
|