Skip to content

Commit 90110b1

Browse files
seanhandleyclaude
andcommitted
Update bindings for H3 v4.0.0
- Update function names to match H3 v4 API - Implement error handling for all functions to use new error code system - Fix edge functions to use directed edge nomenclature - Fix memory handling for C parameters - Re-enable commented functions with proper v4 equivalents 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 05c1cf4 commit 90110b1

File tree

5 files changed

+135
-76
lines changed

5 files changed

+135
-76
lines changed

lib/h3/bindings/private.rb

Lines changed: 39 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -15,63 +15,54 @@ def self.safe_call(out_type, method, *in_args)
1515
out.send("read_#{out_type}".to_sym)
1616
end
1717

18+
# Hierarchy functions
1819
attach_function :cellToParent, [:h3_index, Resolution, H3Index], :h3_error_code
1920
attach_function :compactCells, [H3IndexesIn, H3IndexesOut, :int64], :h3_error_code
20-
# attach_function :destroy_linked_polygon, :destroyLinkedPolygon, [LinkedGeoPolygon], :void
21-
attach_function :edge_length_km, :getHexagonEdgeLengthAvgKm, [Resolution, :pointer], :h3_error_code
21+
attach_function :h3_to_children, :cellToChildren, [:h3_index, Resolution, H3IndexesOut], :h3_error_code
22+
attach_function :max_children, :cellToChildrenSize, [:h3_index, Resolution, :pointer], :h3_error_code
23+
attach_function :max_uncompact_size, :uncompactCellsSize, [H3IndexesIn, :int64, Resolution, :pointer], :h3_error_code
24+
attach_function :uncompactCells, [H3IndexesIn, :size_t, H3IndexesOut, :size_t, Resolution], :h3_error_code
25+
26+
# Indexing functions
2227
attach_function :from_string, :stringToH3, %i[string pointer], :h3_error_code
2328
attach_function :geo_to_h3, :latLngToCell, [LatLng, Resolution, :pointer], :h3_error_code
24-
# attach_function :get_pentagon_indexes, :getPentagonIndexes, [:int, H3IndexesOut], :void
25-
attach_function :hexagon_count, :getNumCells, [Resolution, :pointer], :h3_error_code
26-
attach_function :h3_faces, :getIcosahedronFaces, %i[h3_index buffer_out], :h3_error_code
27-
# attach_function :h3_indexes_from_unidirectional_edge,
28-
# :getH3IndexesFromUnidirectionalEdge,
29-
# [:h3_index, H3IndexesOut], :void
30-
# attach_function :h3_line, :h3Line, [:h3_index, :h3_index, H3IndexesOut], :int
31-
# attach_function :h3_unidirectional_edges_from_hexagon,
32-
# :getH3UnidirectionalEdgesFromHexagon,
33-
# [:h3_index, H3IndexesOut], :void
34-
# attach_function :h3_set_to_linked_geo,
35-
# :h3SetToLinkedGeo,
36-
# [H3IndexesIn, :size_t, LinkedGeoPolygon],
37-
# :void
38-
attach_function :h3_to_children, :cellToChildren, [:h3_index, Resolution, H3IndexesOut], :h3_error_code
3929
attach_function :h3_to_geo, :cellToLatLng, [:h3_index, LatLng], :h3_error_code
4030
attach_function :h3_to_string, :h3ToString, %i[h3_index buffer_out size_t], :h3_error_code
41-
attach_function :h3_to_geo_boundary,
42-
:cellToBoundary,
43-
[:h3_index, CellBoundary],
44-
:h3_error_code
45-
# attach_function :h3_unidirectional_edge_boundary,
46-
# :getH3UnidirectionalEdgeBoundary,
47-
# [:h3_index, CellBoundary], :void
48-
# attach_function :hex_range, :hexRange, [:h3_index, :k_distance, H3IndexesOut], :bool
49-
# attach_function :hex_range_distances,
50-
# :hexRangeDistances,
51-
# [:h3_index, :k_distance, H3IndexesOut, :buffer_out], :bool
52-
# attach_function :hex_ranges,
53-
# :hexRanges,
54-
# [H3IndexesIn, :size_t, :k_distance, H3IndexesOut],
55-
# :bool
56-
# attach_function :hex_ring, :hexRing, [:h3_index, :k_distance, H3IndexesOut], :bool
31+
attach_function :h3_to_geo_boundary, :cellToBoundary, [:h3_index, CellBoundary], :h3_error_code
32+
33+
# Traversal functions
5734
attach_function :k_ring, :gridDisk, [:h3_index, :k_distance, H3IndexesOut], :h3_error_code
58-
# attach_function :k_ring_distances,
59-
# :kRingDistances,
60-
# [:h3_index, :k_distance, H3IndexesOut, :buffer_out],
61-
# :bool
62-
attach_function :max_children, :cellToChildrenSize, [:h3_index, Resolution, :pointer], :h3_error_code
35+
attach_function :k_ring_distances, :gridDiskDistances, [:h3_index, :k_distance, H3IndexesOut, :pointer], :h3_error_code
36+
attach_function :hex_range, :gridDiskUnsafe, [:h3_index, :k_distance, H3IndexesOut], :h3_error_code
37+
attach_function :hex_range_distances, :gridDiskDistancesUnsafe, [:h3_index, :k_distance, H3IndexesOut, :pointer], :h3_error_code
38+
attach_function :hex_ranges, :gridDisksUnsafe, [H3IndexesIn, :size_t, :k_distance, H3IndexesOut], :h3_error_code
39+
attach_function :hex_ring, :gridRingUnsafe, [:h3_index, :k_distance, H3IndexesOut], :h3_error_code
40+
attach_function :h3_line, :gridPathCells, [:h3_index, :h3_index, H3IndexesOut], :h3_error_code
41+
42+
# Directed edge functions (formerly unidirectional_edge)
43+
attach_function :h3_indexes_from_unidirectional_edge, :directedEdgeToCells, [:h3_index, H3IndexesOut], :h3_error_code
44+
attach_function :h3_unidirectional_edges_from_hexagon, :originToDirectedEdges, [:h3_index, H3IndexesOut], :h3_error_code
45+
attach_function :h3_unidirectional_edge_boundary, :directedEdgeToBoundary, [:h3_index, CellBoundary], :h3_error_code
46+
47+
# Miscellaneous functions
48+
attach_function :hexagon_count, :getNumCells, [Resolution, :pointer], :h3_error_code
49+
attach_function :edge_length_km, :getHexagonEdgeLengthAvgKm, [Resolution, :pointer], :h3_error_code
50+
attach_function :edge_length_m, :getHexagonEdgeLengthAvgM, [Resolution, :pointer], :h3_error_code
51+
attach_function :h3_faces, :getIcosahedronFaces, %i[h3_index buffer_out], :h3_error_code
6352
attach_function :max_face_count, :maxFaceCount, %i[h3_index pointer], :h3_error_code
64-
# attach_function :max_polyfill_size,
65-
# :maxPolyfillSize,
66-
# [GeoPolygon, Resolution],
67-
# :int
68-
attach_function :max_uncompact_size, :uncompactCellsSize, [H3IndexesIn, :int64, Resolution, :pointer], :h3_error_code
69-
# attach_function :point_distance_rads, :pointDistRads, [GeoCoord, GeoCoord], :double
70-
# attach_function :point_distance_km, :pointDistKm, [GeoCoord, GeoCoord], :double
71-
# attach_function :point_distance_m, :pointDistM, [GeoCoord, GeoCoord], :double
72-
# attach_function :polyfill, [GeoPolygon, Resolution, H3IndexesOut], :void
53+
attach_function :get_pentagon_indexes, :getPentagonCells, [:int, H3IndexesOut], :h3_error_code
7354
attach_function :res_0_indexes, :getRes0Cells, [H3IndexesOut], :h3_error_code
74-
attach_function :uncompactCells, [H3IndexesIn, :size_t, H3IndexesOut, :size_t, Resolution], :h3_error_code
55+
56+
# Distance functions
57+
attach_function :point_distance_rads, :greatCircleDistanceRads, [LatLng, LatLng], :double
58+
attach_function :point_distance_km, :greatCircleDistanceKm, [LatLng, LatLng], :double
59+
attach_function :point_distance_m, :greatCircleDistanceM, [LatLng, LatLng], :double
60+
61+
# Region functions
62+
attach_function :destroy_linked_polygon, :destroyLinkedMultiPolygon, [LinkedGeoPolygon], :void
63+
attach_function :h3_set_to_linked_geo, :cellsToLinkedMultiPolygon, [H3IndexesIn, :size_t, LinkedGeoPolygon], :h3_error_code
64+
attach_function :max_polyfill_size, :maxPolygonToCellsSize, [GeoPolygon, Resolution, :pointer], :h3_error_code
65+
attach_function :polyfill, :polygonToCells, [GeoPolygon, Resolution, H3IndexesOut], :h3_error_code
7566
end
7667
end
7768
end

lib/h3/inspection.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ def from_string(str)
103103
# @return [String] H3 index in hexadecimal form.
104104
def to_string(h3_index)
105105
h3_str = FFI::MemoryPointer.new(:char, H3_TO_STR_BUF_SIZE)
106-
Bindings::Private.h3_to_string(h3_index, h3_str, H3_TO_STR_BUF_SIZE)
106+
Bindings::Private.h3_to_string(h3_index, h3_str, H3_TO_STR_BUF_SIZE).tap do |code|
107+
Bindings::Error::raise_error(code) unless code.zero?
108+
end
107109
h3_str.read_string
108110
end
109111

lib/h3/miscellaneous.rb

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ def edge_length_km(resolution)
4444
# 3229.482772
4545
#
4646
# @return [Float] Length of edge in metres
47-
# attach_function :edge_length_m, :distanceM, [Resolution], :double
47+
def edge_length_m(resolution)
48+
Bindings::Private.safe_call(:double, :edge_length_m, resolution)
49+
end
4850

4951
# @!method hex_area_km2(resolution)
5052
#
@@ -169,7 +171,7 @@ def hexagon_count(resolution)
169171
# 3.287684056071637e-05
170172
#
171173
# @return [Double] Edge length in rads
172-
# attach_function :exact_edge_length_rads, :exactEdgeLengthRads, %i[h3_index], :double
174+
attach_function :exact_edge_length_rads, :getDirectedEdgeLengthRads, %i[h3_index], :double
173175

174176
# @!method exact_edge_length_km
175177
#
@@ -180,7 +182,7 @@ def hexagon_count(resolution)
180182
# 3.287684056071637e-05
181183
#
182184
# @return [Double] Edge length in kilometres
183-
# attach_function :exact_edge_length_km, :exactEdgeLengthKm, %i[h3_index], :double
185+
attach_function :exact_edge_length_km, :getDirectedEdgeLengthKm, %i[h3_index], :double
184186

185187
# @!method exact_edge_length_m
186188
#
@@ -191,7 +193,7 @@ def hexagon_count(resolution)
191193
# 3.287684056071637e-05
192194
#
193195
# @return [Double] Edge length in metres
194-
# attach_function :exact_edge_length_m, :exactEdgeLengthM, %i[h3_index], :double
196+
attach_function :exact_edge_length_m, :getDirectedEdgeLengthM, %i[h3_index], :double
195197

196198
# Returns the radians distance between two points.
197199
#
@@ -235,7 +237,9 @@ def point_distance_m(origin, destination)
235237
# @return [Array<Integer>] All resolution 0 hexagons (base cells).
236238
def base_cells
237239
out = H3Indexes.of_size(base_cell_count)
238-
Bindings::Private.res_0_indexes(out)
240+
Bindings::Private.res_0_indexes(out).tap do |code|
241+
Bindings::Error::raise_error(code) unless code.zero?
242+
end
239243
out.read
240244
end
241245

@@ -248,7 +252,9 @@ def base_cells
248252
# @return [Array<Integer>] All pentagon indexes at the given resolution.
249253
def pentagons(resolution)
250254
out = H3Indexes.of_size(pentagon_count)
251-
Bindings::Private.get_pentagon_indexes(resolution, out)
255+
Bindings::Private.get_pentagon_indexes(resolution, out).tap do |code|
256+
Bindings::Error::raise_error(code) unless code.zero?
257+
end
252258
out.read
253259
end
254260

lib/h3/traversal.rb

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,24 @@ module Traversal
1010
# Derive the maximum k-ring size for distance k.
1111
#
1212
# @param [Integer] k K value.
13+
# @param [FFI::MemoryPointer] out Optional memory pointer for storing result (for internal use).
1314
#
1415
# @example Derive the maximum k-ring size for k=5
1516
# H3.max_kring_size(5)
1617
# 91
1718
#
18-
# @return [Integer] Maximum k-ring size.
19-
attach_function :max_kring_size, :maxGridDiskSize, %i[k_distance pointer], :h3_error_code
19+
# @return [Integer] Maximum k-ring size if out is nil, otherwise returns error code.
20+
def max_kring_size(k, out = nil)
21+
if out
22+
return Bindings::Private.max_kring_size(k, out)
23+
else
24+
out_ptr = FFI::MemoryPointer.new(:int64)
25+
Bindings::Private.max_kring_size(k, out_ptr).tap do |code|
26+
Bindings::Error::raise_error(code) unless code.zero?
27+
end
28+
out_ptr.read_int64
29+
end
30+
end
2031

2132
# @!method distance(origin, h3_index)
2233
#
@@ -80,10 +91,17 @@ module Traversal
8091
#
8192
# @return [Array<Integer>] Array of H3 indexes within the k-range.
8293
def hex_range(origin, k)
83-
max_hexagons = max_kring_size(k)
84-
out = H3Indexes.of_size(max_hexagons)
85-
pentagonal_distortion = Bindings::Private.hex_range(origin, k, out)
86-
raise(ArgumentError, "Specified hexagon range contains a pentagon") if pentagonal_distortion
94+
max_size = FFI::MemoryPointer.new(:int64)
95+
max_kring_size(k, max_size).tap do |code|
96+
Bindings::Error::raise_error(code) unless code.zero?
97+
end
98+
99+
out = H3Indexes.of_size(max_size.read_int64)
100+
Bindings::Private.hex_range(origin, k, out).tap do |code|
101+
# The 'pentagon distortion' error in H3 v4 is error code 9
102+
raise(ArgumentError, "Specified hexagon range contains a pentagon") if code == 9
103+
Bindings::Error::raise_error(code) unless code.zero?
104+
end
87105
out.read
88106
end
89107

@@ -141,8 +159,13 @@ def k_ring(origin, k)
141159
def hex_ring(origin, k)
142160
max_hexagons = max_hex_ring_size(k)
143161
out = H3Indexes.of_size(max_hexagons)
144-
pentagonal_distortion = Bindings::Private.hex_ring(origin, k, out)
145-
raise(ArgumentError, "The hex ring contains a pentagon") if pentagonal_distortion
162+
163+
Bindings::Private.hex_ring(origin, k, out).tap do |code|
164+
# The 'pentagon distortion' error in H3 v4 is error code 9
165+
raise(ArgumentError, "The hex ring contains a pentagon") if code == 9
166+
Bindings::Error::raise_error(code) unless code.zero?
167+
end
168+
146169
out.read
147170
end
148171

@@ -236,11 +259,20 @@ def hex_ranges(h3_set, k, grouped: true)
236259
#
237260
# @return [Hash] Hex range grouped by distance.
238261
def hex_range_distances(origin, k)
239-
max_out_size = max_kring_size(k)
262+
max_size = FFI::MemoryPointer.new(:int64)
263+
max_kring_size(k, max_size).tap do |code|
264+
Bindings::Error::raise_error(code) unless code.zero?
265+
end
266+
267+
max_out_size = max_size.read_int64
240268
out = H3Indexes.of_size(max_out_size)
241269
distances = FFI::MemoryPointer.new(:int, max_out_size)
242-
pentagonal_distortion = Bindings::Private.hex_range_distances(origin, k, out, distances)
243-
raise(ArgumentError, "Specified hexagon range contains a pentagon") if pentagonal_distortion
270+
271+
Bindings::Private.hex_range_distances(origin, k, out, distances).tap do |code|
272+
# The 'pentagon distortion' error in H3 v4 is error code 9
273+
raise(ArgumentError, "Specified hexagon range contains a pentagon") if code == 9
274+
Bindings::Error::raise_error(code) unless code.zero?
275+
end
244276

245277
hexagons = out.read
246278
distances = distances.read_array_of_int(max_out_size)
@@ -273,10 +305,18 @@ def hex_range_distances(origin, k)
273305
#
274306
# @return [Hash] Hash of k-ring distances grouped by distance.
275307
def k_ring_distances(origin, k)
276-
max_out_size = max_kring_size(k)
308+
max_size = FFI::MemoryPointer.new(:int64)
309+
max_kring_size(k, max_size).tap do |code|
310+
Bindings::Error::raise_error(code) unless code.zero?
311+
end
312+
313+
max_out_size = max_size.read_int64
277314
out = H3Indexes.of_size(max_out_size)
278315
distances = FFI::MemoryPointer.new(:int, max_out_size)
279-
Bindings::Private.k_ring_distances(origin, k, out, distances)
316+
317+
Bindings::Private.k_ring_distances(origin, k, out, distances).tap do |code|
318+
Bindings::Error::raise_error(code) unless code.zero?
319+
end
280320

281321
hexagons = out.read
282322
distances = distances.read_array_of_int(max_out_size)
@@ -305,8 +345,12 @@ def k_ring_distances(origin, k)
305345
def line(origin, destination)
306346
max_hexagons = line_size(origin, destination)
307347
hexagons = H3Indexes.of_size(max_hexagons)
308-
res = Bindings::Private.h3_line(origin, destination, hexagons)
309-
raise(ArgumentError, "Could not compute line") if res.negative?
348+
349+
Bindings::Private.h3_line(origin, destination, hexagons).tap do |code|
350+
# In H3 v4, this could be various error codes
351+
Bindings::Error::raise_error(code) unless code.zero?
352+
end
353+
310354
hexagons.read
311355
end
312356

@@ -322,10 +366,20 @@ def k_rings_for_hex_range(indexes, k)
322366

323367
def hex_ranges_ungrouped(h3_set, k)
324368
h3_set = H3Indexes.with_contents(h3_set)
325-
max_out_size = h3_set.size * max_kring_size(k)
369+
370+
# Calculate max output size
371+
max_out_per_cell = FFI::MemoryPointer.new(:int64)
372+
Bindings::Private.max_kring_size(k, max_out_per_cell).tap do |code|
373+
Bindings::Error::raise_error(code) unless code.zero?
374+
end
375+
max_out_size = h3_set.size * max_out_per_cell.read_int64
376+
326377
out = H3Indexes.of_size(max_out_size)
327-
if Bindings::Private.hex_ranges(h3_set, h3_set.size, k, out)
328-
raise(ArgumentError, "One of the specified hexagon ranges contains a pentagon")
378+
379+
Bindings::Private.hex_ranges(h3_set, h3_set.size, k, out).tap do |code|
380+
# The 'pentagon distortion' error in H3 v4 is error code 9
381+
raise(ArgumentError, "One of the specified hexagon ranges contains a pentagon") if code == 9
382+
Bindings::Error::raise_error(code) unless code.zero?
329383
end
330384

331385
out.read

lib/h3/unidirectional_edges.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ module UnidirectionalEdges
100100
def origin_and_destination_from_unidirectional_edge(edge)
101101
max_hexagons = 2
102102
out = H3Indexes.of_size(max_hexagons)
103-
Bindings::Private.h3_indexes_from_unidirectional_edge(edge, out)
103+
Bindings::Private.h3_indexes_from_unidirectional_edge(edge, out).tap do |code|
104+
Bindings::Error::raise_error(code) unless code.zero?
105+
end
104106
out.read
105107
end
106108

@@ -119,7 +121,9 @@ def origin_and_destination_from_unidirectional_edge(edge)
119121
def unidirectional_edges_from_hexagon(origin)
120122
max_edges = 6
121123
out = H3Indexes.of_size(max_edges)
122-
Bindings::Private.h3_unidirectional_edges_from_hexagon(origin, out)
124+
Bindings::Private.h3_unidirectional_edges_from_hexagon(origin, out).tap do |code|
125+
Bindings::Error::raise_error(code) unless code.zero?
126+
end
123127
out.read
124128
end
125129

@@ -138,7 +142,9 @@ def unidirectional_edges_from_hexagon(origin)
138142
# @return [Array<Array<Float>>] Edge boundary coordinates for a hexagon
139143
def unidirectional_edge_boundary(edge)
140144
geo_boundary = CellBoundary.new
141-
Bindings::Private.h3_unidirectional_edge_boundary(edge, geo_boundary)
145+
Bindings::Private.h3_unidirectional_edge_boundary(edge, geo_boundary).tap do |code|
146+
Bindings::Error::raise_error(code) unless code.zero?
147+
end
142148
geo_boundary[:verts].take(geo_boundary[:num_verts]).map do |d|
143149
[rads_to_degs(d[:lat]), rads_to_degs(d[:lon])]
144150
end

0 commit comments

Comments
 (0)