h3 3.2.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 +7 -0
- data/.codeclimate.yml +19 -0
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.rubocop.yml +255 -0
- data/.travis.yml +19 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +42 -0
- data/LICENSE.md +21 -0
- data/README.md +51 -0
- data/Rakefile +3 -0
- data/h3.gemspec +21 -0
- data/lib/h3.rb +36 -0
- data/lib/h3/bindings.rb +12 -0
- data/lib/h3/bindings/base.rb +15 -0
- data/lib/h3/bindings/private.rb +32 -0
- data/lib/h3/bindings/structs.rb +59 -0
- data/lib/h3/geo_json.rb +169 -0
- data/lib/h3/hierarchy.rb +160 -0
- data/lib/h3/indexing.rb +80 -0
- data/lib/h3/inspection.rb +105 -0
- data/lib/h3/miscellaneous.rb +99 -0
- data/lib/h3/regions.rb +239 -0
- data/lib/h3/traversal.rb +287 -0
- data/lib/h3/unidirectional_edges.rb +146 -0
- data/lib/h3/version.rb +3 -0
- data/spec/geo_json_spec.rb +118 -0
- data/spec/hierarchy_spec.rb +159 -0
- data/spec/indexing_spec.rb +92 -0
- data/spec/inspection_spec.rb +90 -0
- data/spec/miscellaneous_spec.rb +75 -0
- data/spec/region_spec.rb +71 -0
- data/spec/spec_helper.rb +100 -0
- data/spec/support/fixtures/australia.json +1 -0
- data/spec/support/fixtures/banbury.json +1 -0
- data/spec/support/fixtures/banbury_feature.json +93 -0
- data/spec/support/fixtures/banbury_feature_collection.json +98 -0
- data/spec/support/fixtures/banbury_out.json +1 -0
- data/spec/support/fixtures/banbury_without_holes.json +1 -0
- data/spec/support/shared_contexts/constants.rb +4 -0
- data/spec/traversal_spec.rb +343 -0
- data/spec/unidirectional_edges_spec.rb +119 -0
- metadata +155 -0
data/lib/h3/indexing.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module H3
|
2
|
+
# Indexing functions.
|
3
|
+
#
|
4
|
+
# Coordinates are returned in degrees, in the form
|
5
|
+
#
|
6
|
+
# [latitude, longitude]
|
7
|
+
#
|
8
|
+
# @see https://uber.github.io/h3/#/documentation/api-reference/indexing
|
9
|
+
module Indexing
|
10
|
+
# Derive H3 index for the given set of coordinates.
|
11
|
+
#
|
12
|
+
# @param [Array<Integer>] coords A coordinate pair.
|
13
|
+
# @param [Integer] resolution The desired resolution of the H3 index.
|
14
|
+
#
|
15
|
+
# @example Derive the H3 index for the given coordinates.
|
16
|
+
# H3.geo_to_h3([52.24630137198303, -1.7358398437499998], 9)
|
17
|
+
# 617439284584775679
|
18
|
+
#
|
19
|
+
# @raise [ArgumentError] If coordinates are invalid.
|
20
|
+
#
|
21
|
+
# @return [Integer] H3 index.
|
22
|
+
def geo_to_h3(coords, resolution)
|
23
|
+
raise ArgumentError unless coords.is_a?(Array) && coords.count == 2
|
24
|
+
|
25
|
+
lat, lon = coords
|
26
|
+
|
27
|
+
if lat > 90 || lat < -90 || lon > 180 || lon < -180
|
28
|
+
raise(ArgumentError, "Invalid coordinates")
|
29
|
+
end
|
30
|
+
|
31
|
+
coords = Bindings::Structs::GeoCoord.new
|
32
|
+
coords[:lat] = degs_to_rads(lat)
|
33
|
+
coords[:lon] = degs_to_rads(lon)
|
34
|
+
Bindings::Private.geo_to_h3(coords, resolution)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Derive coordinates for a given H3 index.
|
38
|
+
#
|
39
|
+
# The coordinates map to the centre of the hexagon at the given index.
|
40
|
+
#
|
41
|
+
# @param [Integer] h3_index A valid H3 index.
|
42
|
+
#
|
43
|
+
# @example Derive the central coordinates for the given H3 index.
|
44
|
+
# H3.h3_to_geo(617439284584775679)
|
45
|
+
# [52.245519061399506, -1.7363137757391423]
|
46
|
+
#
|
47
|
+
# @return [Array<Integer>] A coordinate pair.
|
48
|
+
def h3_to_geo(h3_index)
|
49
|
+
coords = Bindings::Structs::GeoCoord.new
|
50
|
+
Bindings::Private.h3_to_geo(h3_index, coords)
|
51
|
+
[rads_to_degs(coords[:lat]), rads_to_degs(coords[:lon])]
|
52
|
+
end
|
53
|
+
|
54
|
+
# Derive the geographical boundary as coordinates for a given H3 index.
|
55
|
+
#
|
56
|
+
# This will be a set of 6 coordinate pairs matching the vertexes of the
|
57
|
+
# hexagon represented by the given H3 index.
|
58
|
+
#
|
59
|
+
# If the H3 index is a pentagon, there will be only 5 coordinate pairs returned.
|
60
|
+
#
|
61
|
+
# @param [Integer] h3_index A valid H3 index.
|
62
|
+
#
|
63
|
+
# @example Derive the geographical boundary for the given H3 index.
|
64
|
+
# H3.h3_to_geo_boundary(617439284584775679)
|
65
|
+
# [
|
66
|
+
# [52.247260929171055, -1.736809158397472], [52.24625850761068, -1.7389279144996015],
|
67
|
+
# [52.244516619273476, -1.7384324668792375], [52.243777169245725, -1.7358184256304658],
|
68
|
+
# [52.24477956752282, -1.7336997597088104], [52.246521439109415, -1.7341950448552204]
|
69
|
+
# ]
|
70
|
+
#
|
71
|
+
# @return [Array<Array<Integer>>] An array of six coordinate pairs.
|
72
|
+
def h3_to_geo_boundary(h3_index)
|
73
|
+
geo_boundary = Bindings::Structs::GeoBoundary.new
|
74
|
+
Bindings::Private.h3_to_geo_boundary(h3_index, geo_boundary)
|
75
|
+
geo_boundary[:verts].take(geo_boundary[:num_verts] * 2).map do |d|
|
76
|
+
rads_to_degs(d)
|
77
|
+
end.each_slice(2).to_a
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module H3
|
2
|
+
# Index inspection functions.
|
3
|
+
#
|
4
|
+
# @see https://uber.github.io/h3/#/documentation/api-reference/inspection
|
5
|
+
module Inspection
|
6
|
+
extend H3::Bindings::Base
|
7
|
+
|
8
|
+
H3_TO_STR_BUF_SIZE = 32
|
9
|
+
private_constant :H3_TO_STR_BUF_SIZE
|
10
|
+
|
11
|
+
# @!method h3_resolution(h3_index)
|
12
|
+
#
|
13
|
+
# Derive the resolution of a given H3 index
|
14
|
+
#
|
15
|
+
# @param [Integer] h3_index A valid H3 index
|
16
|
+
#
|
17
|
+
# @example Derive the resolution of a H3 index
|
18
|
+
# H3.h3_resolution(617700440100569087)
|
19
|
+
# 9
|
20
|
+
#
|
21
|
+
# @return [Integer] Resolution of H3 index
|
22
|
+
attach_function :h3_resolution, :h3GetResolution, [ :h3_index ], :int
|
23
|
+
|
24
|
+
# @!method h3_base_cell(h3_index)
|
25
|
+
#
|
26
|
+
# Derives the base cell number of the given H3 index
|
27
|
+
#
|
28
|
+
# @param [Integer] h3_index A valid H3 index
|
29
|
+
#
|
30
|
+
# @example Derive the base cell number of a H3 index
|
31
|
+
# H3.h3_base_cell(617700440100569087)
|
32
|
+
# 20
|
33
|
+
#
|
34
|
+
# @return [Integer] Base cell number
|
35
|
+
attach_function :h3_base_cell, :h3GetBaseCell, [ :h3_index ], :int
|
36
|
+
|
37
|
+
# @!method string_to_h3(h3_string)
|
38
|
+
#
|
39
|
+
# Derives the H3 index for a given hexadecimal string representation.
|
40
|
+
#
|
41
|
+
# @param [String] h3_string A H3 index in hexadecimal form.
|
42
|
+
#
|
43
|
+
# @example Derive the H3 index from the given hexadecimal form.
|
44
|
+
# H3.string_to_h3("8928308280fffff")
|
45
|
+
# 617700169958293503
|
46
|
+
#
|
47
|
+
# @return [Integer] H3 index
|
48
|
+
attach_function :string_to_h3, :stringToH3, [ :string ], :h3_index
|
49
|
+
|
50
|
+
# @!method h3_pentagon?(h3_index)
|
51
|
+
#
|
52
|
+
# Determine whether the given H3 index is a pentagon.
|
53
|
+
#
|
54
|
+
# @param [Integer] h3_index A valid H3 index.
|
55
|
+
#
|
56
|
+
# @example Check if H3 index is a pentagon
|
57
|
+
# H3.h3_pentagon?(585961082523222015)
|
58
|
+
# true
|
59
|
+
#
|
60
|
+
# @return [Boolean] True if the H3 index is a pentagon.
|
61
|
+
attach_function :h3_pentagon, :h3IsPentagon, [ :h3_index ], :bool
|
62
|
+
|
63
|
+
# @!method h3_res_class_3?(h3_index)
|
64
|
+
#
|
65
|
+
# Determine whether the given H3 index has a resolution with
|
66
|
+
# Class III orientation.
|
67
|
+
#
|
68
|
+
# @param [Integer] h3_index A valid H3 index.
|
69
|
+
#
|
70
|
+
# @example Check if H3 index has a class III resolution.
|
71
|
+
# H3.h3_res_class_3?(599686042433355775)
|
72
|
+
# true
|
73
|
+
#
|
74
|
+
# @return [Boolean] True if the H3 index has a class III resolution.
|
75
|
+
attach_function :h3_res_class_3, :h3IsResClassIII, [ :h3_index ], :bool
|
76
|
+
|
77
|
+
# @!method h3_valid?(h3_index)
|
78
|
+
#
|
79
|
+
# Determine whether the given H3 index is valid.
|
80
|
+
#
|
81
|
+
# @param [Integer] h3_index A H3 index.
|
82
|
+
#
|
83
|
+
# @example Check if H3 index is valid
|
84
|
+
# H3.h3_valid?(599686042433355775)
|
85
|
+
# true
|
86
|
+
#
|
87
|
+
# @return [Boolean] True if the H3 index is valid.
|
88
|
+
attach_function :h3_valid, :h3IsValid, [ :h3_index ], :bool
|
89
|
+
|
90
|
+
# Derives the hexadecimal string representation for a given H3 index.
|
91
|
+
#
|
92
|
+
# @param [Integer] h3_index A valid H3 index.
|
93
|
+
#
|
94
|
+
# @example Derive the given hexadecimal form for the H3 index
|
95
|
+
# H3.h3_to_string(617700169958293503)
|
96
|
+
# "89283470dcbffff"
|
97
|
+
#
|
98
|
+
# @return [String] H3 index in hexadecimal form.
|
99
|
+
def h3_to_string(h3_index)
|
100
|
+
h3_str = FFI::MemoryPointer.new(:char, H3_TO_STR_BUF_SIZE)
|
101
|
+
Bindings::Private.h3_to_string(h3_index, h3_str, H3_TO_STR_BUF_SIZE)
|
102
|
+
h3_str.read_string
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module H3
|
2
|
+
# Miscellaneous functions.
|
3
|
+
#
|
4
|
+
# @see https://uber.github.io/h3/#/documentation/api-reference/miscellaneous
|
5
|
+
module Miscellaneous
|
6
|
+
extend H3::Bindings::Base
|
7
|
+
|
8
|
+
# @!method degs_to_rads(degs)
|
9
|
+
#
|
10
|
+
# Convert a number expressed in degrees to its equivalent in radians.
|
11
|
+
#
|
12
|
+
# @param [Float] degs Value expressed in degrees.
|
13
|
+
#
|
14
|
+
# @example Convert degrees value to radians.
|
15
|
+
# H3.degs_to_rads(19.61922082086965)
|
16
|
+
# 0.34242
|
17
|
+
#
|
18
|
+
# @return [Float] Value expressed in radians.
|
19
|
+
attach_function :degs_to_rads, :degsToRads, [ :double ], :double
|
20
|
+
|
21
|
+
# @!method edge_length_km(resolution)
|
22
|
+
#
|
23
|
+
# Derive the length of a hexagon edge in kilometres at the given resolution.
|
24
|
+
#
|
25
|
+
# @param [Integer] resolution Resolution.
|
26
|
+
#
|
27
|
+
# @example Derive length of edge in kilometres
|
28
|
+
# H3.edge_length_km(3)
|
29
|
+
# 59.81085794
|
30
|
+
#
|
31
|
+
# @return [Float] Length of edge in kilometres
|
32
|
+
attach_function :edge_length_km, :edgeLengthKm, [ :int ], :double
|
33
|
+
|
34
|
+
# @!method edge_length_m(resolution)
|
35
|
+
#
|
36
|
+
# Derive the length of a hexagon edge in metres at the given resolution.
|
37
|
+
#
|
38
|
+
# @param [Integer] resolution Resolution.
|
39
|
+
#
|
40
|
+
# @example Derive length of edge in metres
|
41
|
+
# H3.edge_length_m(6)
|
42
|
+
# 3229.482772
|
43
|
+
#
|
44
|
+
# @return [Float] Length of edge in metres
|
45
|
+
attach_function :edge_length_m, :edgeLengthM, [ :int ], :double
|
46
|
+
|
47
|
+
# @!method hex_area_km2(resolution)
|
48
|
+
#
|
49
|
+
# Average hexagon area in square kilometres at the given resolution.
|
50
|
+
#
|
51
|
+
# @param [Integer] resolution Resolution.
|
52
|
+
#
|
53
|
+
# @example Find the square kilometre size at resolution 5
|
54
|
+
# H3.hex_area_km2(5)
|
55
|
+
# 252.9033645
|
56
|
+
#
|
57
|
+
# @return [Float] Average hexagon area in square kilometres.
|
58
|
+
attach_function :hex_area_km2, :hexAreaKm2, [ :int ], :double
|
59
|
+
|
60
|
+
# @!method hex_area_m2(resolution)
|
61
|
+
#
|
62
|
+
# Average hexagon area in square metres at the given resolution.
|
63
|
+
#
|
64
|
+
# @param [Integer] resolution Resolution.
|
65
|
+
#
|
66
|
+
# @example Find the square metre size at resolution 10
|
67
|
+
# H3.hex_area_m2(10)
|
68
|
+
# 15047.5
|
69
|
+
#
|
70
|
+
# @return [Float] Average hexagon area in square metres.
|
71
|
+
attach_function :hex_area_m2, :hexAreaM2, [ :int ], :double
|
72
|
+
|
73
|
+
# @!method num_hexagons(resolution)
|
74
|
+
#
|
75
|
+
# Number of unique H3 indexes at the given resolution.
|
76
|
+
#
|
77
|
+
# @param [Integer] resolution Resolution.
|
78
|
+
#
|
79
|
+
# @example Find number of hexagons at resolution 6
|
80
|
+
# H3.num_hexagons(6)
|
81
|
+
# 14117882
|
82
|
+
#
|
83
|
+
# @return [Integer] Number of unique hexagons
|
84
|
+
attach_function :num_hexagons, :numHexagons, [ :int ], :ulong_long
|
85
|
+
|
86
|
+
# @!method rads_to_degs(rads)
|
87
|
+
#
|
88
|
+
# Convert a number expressed in radians to its equivalent in degrees.
|
89
|
+
#
|
90
|
+
# @param [Float] rads Value expressed in radians.
|
91
|
+
#
|
92
|
+
# @example Convert radians value to degrees.
|
93
|
+
# H3.rads_to_degs(0.34242)
|
94
|
+
# 19.61922082086965
|
95
|
+
#
|
96
|
+
# @return [Float] Value expressed in degrees.
|
97
|
+
attach_function :rads_to_degs, :radsToDegs, [ :double ], :double
|
98
|
+
end
|
99
|
+
end
|
data/lib/h3/regions.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
module H3
|
2
|
+
# Region functions.
|
3
|
+
#
|
4
|
+
# @see https://uber.github.io/h3/#/documentation/api-reference/regions
|
5
|
+
module Regions
|
6
|
+
extend H3::Bindings::Base
|
7
|
+
|
8
|
+
# Derive the maximum number of H3 indexes that could be returned from the input.
|
9
|
+
#
|
10
|
+
# @param [String, Array<Array<Array<Float>>>] geo_polygon Either a GeoJSON string or a coordinates nested array.
|
11
|
+
# @param [Integer] resolution Resolution.
|
12
|
+
#
|
13
|
+
# @example Derive maximum number of hexagons for given GeoJSON document.
|
14
|
+
# geo_json = "{\"type\":\"Polygon\",\"coordinates\":[[[-1.7358398437499998,52.24630137198303],
|
15
|
+
# [-1.8923950195312498,52.05249047600099],[-1.56829833984375,51.891749018068246],
|
16
|
+
# [-1.27716064453125,51.91208502557545],[-1.19476318359375,52.032218104145294],
|
17
|
+
# [-1.24420166015625,52.19413974159753],[-1.5902709960937498,52.24125614966341],
|
18
|
+
# [-1.7358398437499998,52.24630137198303]],[[-1.58203125,52.12590076522272],
|
19
|
+
# [-1.476287841796875,52.12590076522272],[-1.46392822265625,52.075285904832334],
|
20
|
+
# [-1.58203125,52.06937709602395],[-1.58203125,52.12590076522272]],
|
21
|
+
# [[-1.4556884765625,52.01531743663362],[-1.483154296875,51.97642166216334],
|
22
|
+
# [-1.3677978515625,51.96626938051444],[-1.3568115234375,52.0102459910103],
|
23
|
+
# [-1.4556884765625,52.01531743663362]]]}"
|
24
|
+
# H3.max_polyfill_size(geo_json, 9)
|
25
|
+
# 33391
|
26
|
+
#
|
27
|
+
# @example Derive maximum number of hexagons for a nested array of coordinates.
|
28
|
+
# coordinates = [
|
29
|
+
# [
|
30
|
+
# [52.24630137198303, -1.7358398437499998], [52.05249047600099, -1.8923950195312498],
|
31
|
+
# [51.891749018068246, -1.56829833984375], [51.91208502557545, -1.27716064453125],
|
32
|
+
# [52.032218104145294, -1.19476318359375], [52.19413974159753, -1.24420166015625],
|
33
|
+
# [52.24125614966341, -1.5902709960937498], [52.24630137198303, -1.7358398437499998]
|
34
|
+
# ],
|
35
|
+
# [
|
36
|
+
# [52.12590076522272, -1.58203125], [52.12590076522272, -1.476287841796875],
|
37
|
+
# [52.075285904832334, -1.46392822265625], [52.06937709602395, -1.58203125],
|
38
|
+
# [52.12590076522272, -1.58203125]
|
39
|
+
# ],
|
40
|
+
# [
|
41
|
+
# [52.01531743663362, -1.4556884765625], [51.97642166216334, -1.483154296875],
|
42
|
+
# [51.96626938051444, -1.3677978515625], [52.0102459910103, -1.3568115234375],
|
43
|
+
# [52.01531743663362, -1.4556884765625]
|
44
|
+
# ]
|
45
|
+
# ]
|
46
|
+
# H3.max_polyfill_size(coordinates, 9)
|
47
|
+
# 33391
|
48
|
+
#
|
49
|
+
# @return [Integer] Maximum number of hexagons needed to polyfill given area.
|
50
|
+
def max_polyfill_size(geo_polygon, resolution)
|
51
|
+
geo_polygon = geo_json_to_coordinates(geo_polygon) if geo_polygon.is_a?(String)
|
52
|
+
Bindings::Private.max_polyfill_size(build_polygon(geo_polygon), resolution)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Derive a list of H3 indexes that fall within a given geo polygon structure.
|
56
|
+
#
|
57
|
+
# @param [String, Array<Array<Array<Float>>>] geo_polygon Either a GeoJSON string or a coordinates nested array.
|
58
|
+
# @param [Integer] resolution Resolution.
|
59
|
+
#
|
60
|
+
# @example Derive hexagons for given GeoJSON document.
|
61
|
+
# geo_json = "{\"type\":\"Polygon\",\"coordinates\":[[[-1.7358398437499998,52.24630137198303],
|
62
|
+
# [-1.8923950195312498,52.05249047600099],[-1.56829833984375,51.891749018068246],
|
63
|
+
# [-1.27716064453125,51.91208502557545],[-1.19476318359375,52.032218104145294],
|
64
|
+
# [-1.24420166015625,52.19413974159753],[-1.5902709960937498,52.24125614966341],
|
65
|
+
# [-1.7358398437499998,52.24630137198303]],[[-1.58203125,52.12590076522272],
|
66
|
+
# [-1.476287841796875,52.12590076522272],[-1.46392822265625,52.075285904832334],
|
67
|
+
# [-1.58203125,52.06937709602395],[-1.58203125,52.12590076522272]],
|
68
|
+
# [[-1.4556884765625,52.01531743663362],[-1.483154296875,51.97642166216334],
|
69
|
+
# [-1.3677978515625,51.96626938051444],[-1.3568115234375,52.0102459910103],
|
70
|
+
# [-1.4556884765625,52.01531743663362]]]}"
|
71
|
+
# H3.polyfill(geo_json, 5)
|
72
|
+
# [
|
73
|
+
# 599424968551301119, 599424888020664319, 599424970698784767, 599424964256333823,
|
74
|
+
# 599424969625042943, 599425001837297663, 599425000763555839
|
75
|
+
# ]
|
76
|
+
#
|
77
|
+
# @example Derive hexagons for a nested array of coordinates.
|
78
|
+
# coordinates = [
|
79
|
+
# [
|
80
|
+
# [52.24630137198303, -1.7358398437499998], [52.05249047600099, -1.8923950195312498],
|
81
|
+
# [51.891749018068246, -1.56829833984375], [51.91208502557545, -1.27716064453125],
|
82
|
+
# [52.032218104145294, -1.19476318359375], [52.19413974159753, -1.24420166015625],
|
83
|
+
# [52.24125614966341, -1.5902709960937498], [52.24630137198303, -1.7358398437499998]
|
84
|
+
# ],
|
85
|
+
# [
|
86
|
+
# [52.12590076522272, -1.58203125], [52.12590076522272, -1.476287841796875],
|
87
|
+
# [52.075285904832334, -1.46392822265625], [52.06937709602395, -1.58203125],
|
88
|
+
# [52.12590076522272, -1.58203125]
|
89
|
+
# ],
|
90
|
+
# [
|
91
|
+
# [52.01531743663362, -1.4556884765625], [51.97642166216334, -1.483154296875],
|
92
|
+
# [51.96626938051444, -1.3677978515625], [52.0102459910103, -1.3568115234375],
|
93
|
+
# [52.01531743663362, -1.4556884765625]
|
94
|
+
# ]
|
95
|
+
# ]
|
96
|
+
# H3.polyfill(coordinates, 5)
|
97
|
+
# [
|
98
|
+
# 599424968551301119, 599424888020664319, 599424970698784767, 599424964256333823,
|
99
|
+
# 599424969625042943, 599425001837297663, 599425000763555839
|
100
|
+
# ]
|
101
|
+
#
|
102
|
+
# @return [Array<Integer>] Hexagons needed to polyfill given area.
|
103
|
+
def polyfill(geo_polygon, resolution)
|
104
|
+
geo_polygon = geo_json_to_coordinates(geo_polygon) if geo_polygon.is_a?(String)
|
105
|
+
max_size = max_polyfill_size(geo_polygon, resolution)
|
106
|
+
out = FFI::MemoryPointer.new(H3_INDEX, max_size)
|
107
|
+
Bindings::Private.polyfill(build_polygon(geo_polygon), resolution, out)
|
108
|
+
out.read_array_of_ulong_long(max_size).reject(&:zero?)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Derive a nested array of coordinates from a list of H3 indexes.
|
112
|
+
#
|
113
|
+
# @param [Array<Integer>] h3_indexes A list of H3 indexes.
|
114
|
+
#
|
115
|
+
# @example Get a set of coordinates from a given list of H3 indexes.
|
116
|
+
# h3_indexes = [
|
117
|
+
# 599424968551301119, 599424888020664319, 599424970698784767,
|
118
|
+
# 599424964256333823, 599424969625042943, 599425001837297663,
|
119
|
+
# 599425000763555839
|
120
|
+
# ]
|
121
|
+
# H3.h3_set_to_linked_geo(h3_indexes)
|
122
|
+
# [
|
123
|
+
# [
|
124
|
+
# [52.24425364171531, -1.6470570189756442], [52.19515282473624, -1.7508281227260887],
|
125
|
+
# [52.10973325363767, -1.7265910686763437], [52.06042870859474, -1.8301115887419024],
|
126
|
+
# [51.97490199314513, -1.8057974545517919], [51.9387204737266, -1.6783497689296265],
|
127
|
+
# [51.853128001893175, -1.654344796003053], [51.81682604752331, -1.5274195136674955],
|
128
|
+
# [51.866019925789956, -1.424329996292339], [51.829502535462176, -1.2977583914075301],
|
129
|
+
# [51.87843896218677, -1.1946402363628545], [51.96394676922824, -1.21787542551618],
|
130
|
+
# [52.01267958543637, -1.1145114691876956], [52.09808058649905, -1.1376655003242908],
|
131
|
+
# [52.134791926560325, -1.26456988729442], [52.22012854584846, -1.2880298658365215],
|
132
|
+
# [52.25672060485973, -1.4154623025177386], [52.20787927927604, -1.5192658757247421]
|
133
|
+
# ]
|
134
|
+
# ]
|
135
|
+
#
|
136
|
+
# @return [Array<Array<Array<Float>>>] Nested array of coordinates.
|
137
|
+
def h3_set_to_linked_geo(h3_indexes)
|
138
|
+
h3_indexes.uniq!
|
139
|
+
linked_geo_polygon = Bindings::Structs::LinkedGeoPolygon.new
|
140
|
+
FFI::MemoryPointer.new(H3_INDEX, h3_indexes.size) do |hexagons_ptr|
|
141
|
+
hexagons_ptr.write_array_of_ulong_long(h3_indexes)
|
142
|
+
Bindings::Private.h3_set_to_linked_geo(hexagons_ptr, h3_indexes.size, linked_geo_polygon)
|
143
|
+
end
|
144
|
+
|
145
|
+
extract_linked_geo_polygon(linked_geo_polygon).first
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def extract_linked_geo_polygon(linked_geo_polygon)
|
151
|
+
return if linked_geo_polygon.null?
|
152
|
+
|
153
|
+
geo_polygons = [linked_geo_polygon]
|
154
|
+
|
155
|
+
until linked_geo_polygon[:next].null? do
|
156
|
+
geo_polygons << linked_geo_polygon[:next]
|
157
|
+
linked_geo_polygon = linked_geo_polygon[:next]
|
158
|
+
end
|
159
|
+
|
160
|
+
geo_polygons.map(&method(:extract_geo_polygon))
|
161
|
+
end
|
162
|
+
|
163
|
+
def extract_geo_polygon(geo_polygon)
|
164
|
+
extract_linked_geo_loop(geo_polygon[:first]) unless geo_polygon[:first].null?
|
165
|
+
end
|
166
|
+
|
167
|
+
def extract_linked_geo_loop(linked_geo_loop)
|
168
|
+
return if linked_geo_loop.null?
|
169
|
+
|
170
|
+
geo_loops = [linked_geo_loop]
|
171
|
+
|
172
|
+
until linked_geo_loop[:next].null? do
|
173
|
+
geo_loops << linked_geo_loop[:next]
|
174
|
+
linked_geo_loop = linked_geo_loop[:next]
|
175
|
+
end
|
176
|
+
|
177
|
+
geo_loops.map(&method(:extract_geo_loop))
|
178
|
+
end
|
179
|
+
|
180
|
+
def extract_geo_loop(geo_loop)
|
181
|
+
extract_linked_geo_coord(geo_loop[:first]) unless geo_loop[:first].null?
|
182
|
+
end
|
183
|
+
|
184
|
+
def extract_linked_geo_coord(linked_geo_coord)
|
185
|
+
return if linked_geo_coord.null?
|
186
|
+
|
187
|
+
geo_coords = [linked_geo_coord]
|
188
|
+
|
189
|
+
until linked_geo_coord[:next].null? do
|
190
|
+
geo_coords << linked_geo_coord[:next]
|
191
|
+
linked_geo_coord = linked_geo_coord[:next]
|
192
|
+
end
|
193
|
+
|
194
|
+
geo_coords.map(&method(:extract_geo_coord))
|
195
|
+
end
|
196
|
+
|
197
|
+
def extract_geo_coord(geo_coord)
|
198
|
+
[
|
199
|
+
rads_to_degs(geo_coord[:vertex][:lat]),
|
200
|
+
rads_to_degs(geo_coord[:vertex][:lon])
|
201
|
+
]
|
202
|
+
end
|
203
|
+
|
204
|
+
def build_polygon(input)
|
205
|
+
outline, *holes = input
|
206
|
+
geo_polygon = Bindings::Structs::GeoPolygon.new
|
207
|
+
geo_polygon[:geofence] = build_geofence(outline)
|
208
|
+
len = holes.count
|
209
|
+
geo_polygon[:num_holes] = len
|
210
|
+
geofences = holes.map(&method(:build_geofence))
|
211
|
+
ptr = FFI::MemoryPointer.new(Bindings::Structs::GeoFence, len)
|
212
|
+
fence_structs = geofences.count.times.map do |i|
|
213
|
+
Bindings::Structs::GeoFence.new(ptr + i * Bindings::Structs::GeoFence.size())
|
214
|
+
end
|
215
|
+
geofences.each_with_index do |geofence, i|
|
216
|
+
fence_structs[i][:num_verts] = geofence[:num_verts]
|
217
|
+
fence_structs[i][:verts] = geofence[:verts]
|
218
|
+
end
|
219
|
+
geo_polygon[:holes] = ptr
|
220
|
+
geo_polygon
|
221
|
+
end
|
222
|
+
|
223
|
+
def build_geofence(input)
|
224
|
+
geo_fence = Bindings::Structs::GeoFence.new
|
225
|
+
len = input.count
|
226
|
+
geo_fence[:num_verts] = len
|
227
|
+
ptr = FFI::MemoryPointer.new(Bindings::Structs::GeoCoord, len)
|
228
|
+
coords = 0.upto(len).map do |i|
|
229
|
+
Bindings::Structs::GeoCoord.new(ptr + i * Bindings::Structs::GeoCoord.size)
|
230
|
+
end
|
231
|
+
input.each_with_index do |(lat, lon), i|
|
232
|
+
coords[i][:lat] = degs_to_rads(lat)
|
233
|
+
coords[i][:lon] = degs_to_rads(lon)
|
234
|
+
end
|
235
|
+
geo_fence[:verts] = ptr
|
236
|
+
geo_fence
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|