h3 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|