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
@@ -0,0 +1,159 @@
|
|
1
|
+
RSpec.describe H3 do
|
2
|
+
include_context "constants"
|
3
|
+
|
4
|
+
describe ".h3_to_parent" do
|
5
|
+
let(:h3_index) { "89283082993ffff".to_i(16) }
|
6
|
+
let(:parent_resolution) { 8 }
|
7
|
+
let(:result) { "8828308299fffff".to_i(16) }
|
8
|
+
|
9
|
+
subject(:h3_to_parent) { H3.h3_to_parent(h3_index, parent_resolution) }
|
10
|
+
|
11
|
+
it { is_expected.to eq(result) }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe ".h3_to_children" do
|
15
|
+
let(:h3_index) { "8928308280fffff".to_i(16) }
|
16
|
+
|
17
|
+
subject(:h3_to_children) { H3.h3_to_children(h3_index, child_resolution) }
|
18
|
+
|
19
|
+
context "when resolution is 3" do
|
20
|
+
let(:child_resolution) { 3 }
|
21
|
+
let(:count) { 0 }
|
22
|
+
|
23
|
+
it "has 0 children" do
|
24
|
+
expect(h3_to_children.count).to eq count
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when resolution is 9" do
|
29
|
+
let(:child_resolution) { 9 }
|
30
|
+
let(:count) { 1 }
|
31
|
+
let(:expected) { "8928308280fffff".to_i(16) }
|
32
|
+
|
33
|
+
it "has 1 child" do
|
34
|
+
expect(h3_to_children.count).to eq count
|
35
|
+
end
|
36
|
+
|
37
|
+
it "is the expected value" do
|
38
|
+
expect(h3_to_children.first).to eq expected
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when resolution is 10" do
|
43
|
+
let(:child_resolution) { 10 }
|
44
|
+
let(:count) { 7 }
|
45
|
+
|
46
|
+
it "has 7 children" do
|
47
|
+
expect(h3_to_children.count).to eq count
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when resolution is 15" do
|
52
|
+
let(:child_resolution) { 15 }
|
53
|
+
let(:count) { 117649 }
|
54
|
+
|
55
|
+
it "has 117649 children" do
|
56
|
+
expect(h3_to_children.count).to eq count
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe ".max_h3_to_children_size" do
|
62
|
+
let(:h3_index) { "8928308280fffff".to_i(16) }
|
63
|
+
|
64
|
+
subject(:h3_to_children) { H3.max_h3_to_children_size(h3_index, child_resolution) }
|
65
|
+
|
66
|
+
context "when resolution is 3" do
|
67
|
+
let(:child_resolution) { 3 }
|
68
|
+
let(:count) { 0 }
|
69
|
+
|
70
|
+
it { is_expected.to eq(count) }
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when resolution is 9" do
|
74
|
+
let(:child_resolution) { 9 }
|
75
|
+
let(:count) { 1 }
|
76
|
+
|
77
|
+
it { is_expected.to eq(count) }
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when resolution is 10" do
|
81
|
+
let(:child_resolution) { 10 }
|
82
|
+
let(:count) { 7 }
|
83
|
+
|
84
|
+
it { is_expected.to eq(count) }
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when resolution is 15" do
|
88
|
+
let(:child_resolution) { 15 }
|
89
|
+
let(:count) { 117649 }
|
90
|
+
|
91
|
+
it { is_expected.to eq(count) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe ".compact" do
|
96
|
+
let(:h3_index) { "89283470c27ffff".to_i(16) }
|
97
|
+
let(:k) { 9 }
|
98
|
+
let(:uncompacted) do
|
99
|
+
H3.k_ring(h3_index, k)
|
100
|
+
end
|
101
|
+
|
102
|
+
subject(:compact) { H3.compact(uncompacted) }
|
103
|
+
|
104
|
+
it "has an uncompacted size of 271" do
|
105
|
+
expect(uncompacted.size).to eq 271
|
106
|
+
end
|
107
|
+
|
108
|
+
it "has a compacted size of 73" do
|
109
|
+
expect(compact.size).to eq 73
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe ".uncompact" do
|
114
|
+
let(:h3_index) { "89283470c27ffff".to_i(16) }
|
115
|
+
let(:resolution) { 9 }
|
116
|
+
let(:uncompacted) do
|
117
|
+
H3.k_ring(h3_index, resolution)
|
118
|
+
end
|
119
|
+
let(:compacted) do
|
120
|
+
H3.compact(uncompacted)
|
121
|
+
end
|
122
|
+
|
123
|
+
subject(:uncompact) { H3.uncompact(compacted, resolution) }
|
124
|
+
|
125
|
+
it "has an uncompacted size of 271" do
|
126
|
+
expect(uncompact.size).to eq 271
|
127
|
+
end
|
128
|
+
|
129
|
+
it "has a compacted size of 73" do
|
130
|
+
expect(compacted.size).to eq 73
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when resolution is incorrect for index" do
|
134
|
+
let(:resolution) { 8 }
|
135
|
+
|
136
|
+
it "raises error" do
|
137
|
+
expect { uncompact }.to raise_error(ArgumentError)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe ".max_uncompact_size" do
|
143
|
+
let(:h3_indexes) { ["8928308280fffff", "89283470893ffff"].map { |i| i.to_i(16) } }
|
144
|
+
let(:resolution) { 9 }
|
145
|
+
let(:result) { 2 }
|
146
|
+
|
147
|
+
subject(:max_uncompact_size) { H3.max_uncompact_size(h3_indexes, resolution) }
|
148
|
+
|
149
|
+
it { is_expected.to eq result }
|
150
|
+
|
151
|
+
context "when resolution is incorrect for index" do
|
152
|
+
let(:resolution) { 8 }
|
153
|
+
|
154
|
+
it "raises an error" do
|
155
|
+
expect { max_uncompact_size }.to raise_error(ArgumentError)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
RSpec.describe H3 do
|
2
|
+
include_context "constants"
|
3
|
+
|
4
|
+
describe ".geo_to_h3" do
|
5
|
+
let(:resolution) { 8 }
|
6
|
+
let(:coords) { [53.959130, -1.079230]}
|
7
|
+
let(:result) { valid_h3_index }
|
8
|
+
|
9
|
+
subject(:geo_to_h3) { H3.geo_to_h3(coords, resolution) }
|
10
|
+
|
11
|
+
it { is_expected.to eq(result) }
|
12
|
+
|
13
|
+
context "when given more than 2 values" do
|
14
|
+
let(:coords) { [1, 2, 3] }
|
15
|
+
|
16
|
+
it "raises an error" do
|
17
|
+
expect { geo_to_h3 }.to raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when given a non array" do
|
22
|
+
let(:coords) { "boom" }
|
23
|
+
|
24
|
+
it "raises an error" do
|
25
|
+
expect { geo_to_h3 }.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when given bad coordinates" do
|
30
|
+
let(:coords) { [-1.1323222, 190.1020102] }
|
31
|
+
|
32
|
+
it "raises an error" do
|
33
|
+
expect { geo_to_h3 }.to raise_error(ArgumentError)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe ".h3_to_geo" do
|
39
|
+
let(:h3_index) { valid_h3_index }
|
40
|
+
let(:expected_lat) { 53.95860421941 }
|
41
|
+
let(:expected_lon) { -1.08119564709 }
|
42
|
+
|
43
|
+
subject(:h3_to_geo) { H3.h3_to_geo(h3_index) }
|
44
|
+
|
45
|
+
it "should return the expected latitude" do
|
46
|
+
expect(h3_to_geo[0]).to be_within(0.000001).of(expected_lat)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return the expected longitude" do
|
50
|
+
expect(h3_to_geo[1]).to be_within(0.000001).of(expected_lon)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when given an invalid h3_index" do
|
54
|
+
let(:h3_index) { "boom" }
|
55
|
+
|
56
|
+
it "raises an error" do
|
57
|
+
expect { h3_to_geo }.to raise_error(TypeError)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when given an index that's too large" do
|
62
|
+
let(:h3_index) { too_long_number }
|
63
|
+
|
64
|
+
it "raises an error" do
|
65
|
+
expect { h3_to_geo }.to raise_error(RangeError)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe ".h3_to_geo_boundary" do
|
71
|
+
let(:h3_index) { "85283473fffffff".to_i(16) }
|
72
|
+
let(:expected) do
|
73
|
+
[
|
74
|
+
[37.2713558667319, -121.91508032705622],
|
75
|
+
[37.353926450852256, -121.8622232890249],
|
76
|
+
[37.42834118609435, -121.92354999630156],
|
77
|
+
[37.42012867767779, -122.03773496427027],
|
78
|
+
[37.33755608435299, -122.090428929044],
|
79
|
+
[37.26319797461824, -122.02910130918998]
|
80
|
+
]
|
81
|
+
end
|
82
|
+
|
83
|
+
subject(:h3_to_geo_boundary) { H3.h3_to_geo_boundary(h3_index) }
|
84
|
+
|
85
|
+
it "matches expected boundary coordinates" do
|
86
|
+
h3_to_geo_boundary.zip(expected) do |(lat, lon), (exp_lat, exp_lon)|
|
87
|
+
expect(lat).to be_within(0.000001).of(exp_lat)
|
88
|
+
expect(lon).to be_within(0.000001).of(exp_lon)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
RSpec.describe H3 do
|
2
|
+
include_context "constants"
|
3
|
+
|
4
|
+
describe ".h3_resolution" do
|
5
|
+
let(:h3_index) { valid_h3_index }
|
6
|
+
let(:result) { 8 }
|
7
|
+
|
8
|
+
subject(:h3_resolution) { H3.h3_resolution(h3_index) }
|
9
|
+
|
10
|
+
it { is_expected.to eq(result) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe ".h3_base_cell" do
|
14
|
+
let(:h3_index) { valid_h3_index }
|
15
|
+
let(:result) { 12 }
|
16
|
+
|
17
|
+
subject(:h3_base_cell) { H3.h3_base_cell(h3_index) }
|
18
|
+
|
19
|
+
it { is_expected.to eq(result) }
|
20
|
+
end
|
21
|
+
|
22
|
+
describe ".string_to_h3" do
|
23
|
+
let(:h3_index) { "8928308280fffff"}
|
24
|
+
let(:result) { h3_index.to_i(16) }
|
25
|
+
|
26
|
+
subject(:string_to_h3) { H3.string_to_h3(h3_index) }
|
27
|
+
|
28
|
+
it { is_expected.to eq(result) }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ".h3_to_string" do
|
32
|
+
let(:h3_index) { "8928308280fffff".to_i(16) }
|
33
|
+
let(:result) { h3_index.to_s(16) }
|
34
|
+
|
35
|
+
subject(:h3_to_string) { H3.h3_to_string(h3_index) }
|
36
|
+
|
37
|
+
it { is_expected.to eq(result) }
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ".h3_valid?" do
|
41
|
+
let(:h3_index) { valid_h3_index }
|
42
|
+
let(:result) { true }
|
43
|
+
|
44
|
+
subject(:h3_valid?) { H3.h3_valid?(h3_index) }
|
45
|
+
|
46
|
+
it { is_expected.to eq(result) }
|
47
|
+
|
48
|
+
context "when given an invalid h3_index" do
|
49
|
+
let(:h3_index) { 1 }
|
50
|
+
|
51
|
+
let(:result) { false }
|
52
|
+
|
53
|
+
it "returns the expected result" do
|
54
|
+
expect(h3_valid?).to eq(result)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe ".h3_res_class_3?" do
|
60
|
+
let(:h3_index) { "8928308280fffff".to_i(16) }
|
61
|
+
let(:result) { true }
|
62
|
+
|
63
|
+
subject(:h3_res_class_3) { H3.h3_res_class_3?(h3_index) }
|
64
|
+
|
65
|
+
it { is_expected.to eq(result) }
|
66
|
+
|
67
|
+
context "when the h3 index is not class III" do
|
68
|
+
let(:h3_index) { "8828308280fffff".to_i(16) }
|
69
|
+
let(:result) { false }
|
70
|
+
|
71
|
+
it { is_expected.to eq(result) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe ".h3_pentagon?" do
|
76
|
+
let(:h3_index) { "821c07fffffffff".to_i(16) }
|
77
|
+
let(:result) { true }
|
78
|
+
|
79
|
+
subject(:h3_pentagon?) { H3.h3_pentagon?(h3_index) }
|
80
|
+
|
81
|
+
it { is_expected.to eq(result) }
|
82
|
+
|
83
|
+
context "when the h3 index is not a pentagon" do
|
84
|
+
let(:h3_index) { "8928308280fffff".to_i(16) }
|
85
|
+
let(:result) { false }
|
86
|
+
|
87
|
+
it { is_expected.to eq(result) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
RSpec.describe H3 do
|
2
|
+
include_context "constants"
|
3
|
+
|
4
|
+
describe ".num_hexagons" do
|
5
|
+
let(:resolution) { 2 }
|
6
|
+
let(:result) { 5882 }
|
7
|
+
|
8
|
+
subject(:num_hexagons) { H3.num_hexagons(resolution) }
|
9
|
+
|
10
|
+
it { is_expected.to eq(result) }
|
11
|
+
|
12
|
+
context "when given an invalid resolution" do
|
13
|
+
let(:resolution) { too_long_number }
|
14
|
+
let(:result) { false }
|
15
|
+
|
16
|
+
it "returns the expected result" do
|
17
|
+
expect { num_hexagons }.to raise_error(RangeError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe ".degs_to_rads" do
|
23
|
+
let(:degs) { 100 }
|
24
|
+
let(:result) { 1.7453292519943295 }
|
25
|
+
|
26
|
+
subject(:degs_to_rads) { H3.degs_to_rads(degs) }
|
27
|
+
|
28
|
+
it { is_expected.to eq(result) }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ".rads_to_degs" do
|
32
|
+
let(:rads) { 1.7453292519943295 }
|
33
|
+
let(:result) { 100 }
|
34
|
+
|
35
|
+
subject(:rads_to_degs) { H3.rads_to_degs(rads) }
|
36
|
+
|
37
|
+
it { is_expected.to eq(result) }
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ".hex_area_km2" do
|
41
|
+
let(:resolution) { 2 }
|
42
|
+
let(:result) { 86745.85403 }
|
43
|
+
|
44
|
+
subject(:hex_area_km2) { H3.hex_area_km2(resolution) }
|
45
|
+
|
46
|
+
it { is_expected.to eq(result) }
|
47
|
+
end
|
48
|
+
|
49
|
+
describe ".hex_area_m2" do
|
50
|
+
let(:resolution) { 2 }
|
51
|
+
let(:result) { 86745854035.0 }
|
52
|
+
|
53
|
+
subject(:hex_area_m2) { H3.hex_area_m2(resolution) }
|
54
|
+
|
55
|
+
it { is_expected.to eq(result) }
|
56
|
+
end
|
57
|
+
|
58
|
+
describe ".edge_length_km" do
|
59
|
+
let(:resolution) { 2 }
|
60
|
+
let(:result) { 158.2446558 }
|
61
|
+
|
62
|
+
subject(:edge_length_km) { H3.edge_length_km(resolution) }
|
63
|
+
|
64
|
+
it { is_expected.to eq(result) }
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".edge_length_m" do
|
68
|
+
let(:resolution) { 2 }
|
69
|
+
let(:result) { 158244.6558 }
|
70
|
+
|
71
|
+
subject(:edge_length_m) { H3.edge_length_m(resolution) }
|
72
|
+
|
73
|
+
it { is_expected.to eq(result) }
|
74
|
+
end
|
75
|
+
end
|
data/spec/region_spec.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
RSpec.describe H3 do
|
2
|
+
include_context "constants"
|
3
|
+
|
4
|
+
describe ".polyfill" do
|
5
|
+
let(:geojson) do
|
6
|
+
File.read(File.join(File.dirname(__FILE__), "support/fixtures/banbury_without_holes.json"))
|
7
|
+
end
|
8
|
+
let(:resolution) { 9 }
|
9
|
+
let(:expected_count) { 14_369 }
|
10
|
+
|
11
|
+
subject(:polyfill) { H3.polyfill(geojson, resolution) }
|
12
|
+
|
13
|
+
it "has the correct number of hexagons" do
|
14
|
+
expect(polyfill.count).to eq expected_count
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when banbury area has two holes in it" do
|
18
|
+
let(:geojson) do
|
19
|
+
File.read(File.join(File.dirname(__FILE__), "support/fixtures/banbury.json"))
|
20
|
+
end
|
21
|
+
let(:expected_count) { 13_526 }
|
22
|
+
|
23
|
+
it "has fewer hexagons" do
|
24
|
+
expect(polyfill.count).to eq expected_count
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when polyfilling australia" do
|
29
|
+
let(:geojson) do
|
30
|
+
File.read(File.join(File.dirname(__FILE__), "support/fixtures/australia.json"))
|
31
|
+
end
|
32
|
+
let(:expect_count) { 92 }
|
33
|
+
|
34
|
+
it "has the correct number of hexagons" do
|
35
|
+
expect(polyfill.count).to eq expect_count
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ".max_polyfill_size" do
|
41
|
+
let(:geojson) do
|
42
|
+
File.read(File.join(File.dirname(__FILE__), "support/fixtures/banbury.json"))
|
43
|
+
end
|
44
|
+
let(:resolution) { 9 }
|
45
|
+
let(:expected_count) { 33_391 }
|
46
|
+
|
47
|
+
subject(:max_polyfill_size) { H3.max_polyfill_size(geojson, resolution) }
|
48
|
+
|
49
|
+
it "has the correct number of hexagons" do
|
50
|
+
expect(max_polyfill_size).to eq expected_count
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe ".h3_set_to_linked_geo" do
|
55
|
+
let(:geojson) do
|
56
|
+
File.read(File.join(File.dirname(__FILE__), "support/fixtures/banbury.json"))
|
57
|
+
end
|
58
|
+
let(:resolution) { 8 }
|
59
|
+
let(:hexagons) { H3.polyfill(geojson, resolution) }
|
60
|
+
|
61
|
+
subject(:h3_set_to_linked_geo) { H3.h3_set_to_linked_geo(hexagons) }
|
62
|
+
|
63
|
+
it "has 3 outlines" do
|
64
|
+
h3_set_to_linked_geo.count == 3
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can be converted to GeoJSON" do
|
68
|
+
expect(H3.coordinates_to_geo_json(h3_set_to_linked_geo)).to be_truthy
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|