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.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "ffi"
|
2
|
+
require "rgeo/geo_json"
|
3
|
+
|
4
|
+
require "h3/bindings"
|
5
|
+
require "h3/geo_json"
|
6
|
+
require "h3/hierarchy"
|
7
|
+
require "h3/indexing"
|
8
|
+
require "h3/inspection"
|
9
|
+
require "h3/miscellaneous"
|
10
|
+
require "h3/regions"
|
11
|
+
require "h3/traversal"
|
12
|
+
require "h3/unidirectional_edges"
|
13
|
+
|
14
|
+
# The main H3 namespace.
|
15
|
+
#
|
16
|
+
# All public methods for the library are defined here.
|
17
|
+
#
|
18
|
+
# @see https://uber.github.io/h3/#/documentation/overview/introduction
|
19
|
+
module H3
|
20
|
+
class << self
|
21
|
+
include GeoJSON
|
22
|
+
include Hierarchy
|
23
|
+
include Miscellaneous
|
24
|
+
include Indexing
|
25
|
+
include Inspection
|
26
|
+
include Regions
|
27
|
+
include Traversal
|
28
|
+
include UnidirectionalEdges
|
29
|
+
end
|
30
|
+
|
31
|
+
PREDICATES = %i(h3_indexes_neighbors h3_pentagon h3_res_class_3
|
32
|
+
h3_unidirectional_edge_valid h3_valid).freeze
|
33
|
+
PREDICATES.each do |predicate|
|
34
|
+
singleton_class.send(:alias_method, "#{predicate}?".to_sym, predicate)
|
35
|
+
end
|
36
|
+
end
|
data/lib/h3/bindings.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "h3/bindings/base"
|
2
|
+
require "h3/bindings/structs"
|
3
|
+
require "h3/bindings/private"
|
4
|
+
|
5
|
+
module H3
|
6
|
+
# Internal bindings related modules and classes.
|
7
|
+
#
|
8
|
+
# These are intended to be used by the library's public methods
|
9
|
+
# and not to be used directly by client code.
|
10
|
+
module Bindings
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module H3
|
2
|
+
module Bindings
|
3
|
+
# Base for FFI Bindings.
|
4
|
+
#
|
5
|
+
# When extended, this module sets up FFI to use the H3 C library.
|
6
|
+
module Base
|
7
|
+
def self.extended(base)
|
8
|
+
base.extend FFI::Library
|
9
|
+
base.ffi_lib ["libh3", "libh3.1"]
|
10
|
+
base.typedef :ulong_long, :h3_index
|
11
|
+
base.const_set('H3_INDEX', :ulong_long)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module H3
|
2
|
+
module Bindings
|
3
|
+
# Private H3 functions which should not be called directly.
|
4
|
+
#
|
5
|
+
# This module provides bindings that do not have to be invoked directly by clients
|
6
|
+
# of the library. They are used only internally to provide related public interface.
|
7
|
+
module Private
|
8
|
+
extend H3::Bindings::Base
|
9
|
+
|
10
|
+
attach_function :compact, [:pointer, :pointer, :int], :bool
|
11
|
+
attach_function :geo_to_h3, :geoToH3, [ Bindings::Structs::GeoCoord.by_ref, :int ], :h3_index
|
12
|
+
attach_function :h3_indexes_from_unidirectional_edge, :getH3IndexesFromUnidirectionalEdge, [ :h3_index, :pointer ], :void
|
13
|
+
attach_function :h3_unidirectional_edges_from_hexagon, :getH3UnidirectionalEdgesFromHexagon, [ :h3_index, :pointer ], :void
|
14
|
+
attach_function :h3_set_to_linked_geo, :h3SetToLinkedGeo, [ :pointer, :int, Bindings::Structs::LinkedGeoPolygon.by_ref ], :void
|
15
|
+
attach_function :h3_to_children, :h3ToChildren, [ :h3_index, :int, :pointer ], :void
|
16
|
+
attach_function :h3_to_geo, :h3ToGeo, [ :h3_index, Bindings::Structs::GeoCoord.by_ref ], :void
|
17
|
+
attach_function :h3_to_string, :h3ToString, [ :h3_index, :pointer, :int], :void
|
18
|
+
attach_function :h3_to_geo_boundary, :h3ToGeoBoundary, [:h3_index, Bindings::Structs::GeoBoundary.by_ref ], :void
|
19
|
+
attach_function :h3_unidirectional_edge_boundary, :getH3UnidirectionalEdgeBoundary, [:h3_index, :pointer], :void
|
20
|
+
attach_function :hex_range, :hexRange, [ :h3_index, :int, :pointer ], :bool
|
21
|
+
attach_function :hex_range_distances, :hexRangeDistances, [:h3_index, :int, :pointer, :pointer], :bool
|
22
|
+
attach_function :hex_ranges, :hexRanges, [ :pointer, :int, :int, :pointer ], :bool
|
23
|
+
attach_function :hex_ring, :hexRing, [:h3_index, :int, :pointer], :bool
|
24
|
+
attach_function :k_ring, :kRing, [:h3_index, :int, :pointer], :void
|
25
|
+
attach_function :k_ring_distances, :kRingDistances, [:h3_index, :int, :pointer, :pointer], :bool
|
26
|
+
attach_function :max_polyfill_size, :maxPolyfillSize, [Bindings::Structs::GeoPolygon.by_ref, :int], :int
|
27
|
+
attach_function :max_uncompact_size, :maxUncompactSize, [:pointer, :int, :int], :int
|
28
|
+
attach_function :polyfill, [ :pointer, :int, :pointer ], :void
|
29
|
+
attach_function :uncompact, [:pointer, :int, :pointer, :int, :int], :bool
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module H3
|
2
|
+
module Bindings
|
3
|
+
# FFI Structs.
|
4
|
+
#
|
5
|
+
# These match the structs defined in H3's header file and are required
|
6
|
+
# to correctly interact with the library's functions.
|
7
|
+
module Structs
|
8
|
+
extend FFI::Library
|
9
|
+
|
10
|
+
class GeoCoord < FFI::Struct
|
11
|
+
layout :lat, :double,
|
12
|
+
:lon, :double
|
13
|
+
end
|
14
|
+
|
15
|
+
class GeoBoundary < FFI::Struct
|
16
|
+
layout :num_verts, :int,
|
17
|
+
:verts, [:double, 20] # array of GeoCoord structs (must be fixed length)
|
18
|
+
end
|
19
|
+
|
20
|
+
class GeoFence < FFI::Struct
|
21
|
+
layout :num_verts, :int,
|
22
|
+
:verts, :pointer # array of GeoCoord structs
|
23
|
+
end
|
24
|
+
|
25
|
+
class GeoPolygon < FFI::Struct
|
26
|
+
layout :geofence, GeoFence,
|
27
|
+
:num_holes, :int,
|
28
|
+
:holes, :pointer # array of GeoFence structs
|
29
|
+
end
|
30
|
+
|
31
|
+
class GeoMultiPolygon < FFI::Struct
|
32
|
+
layout :num_polygons, :int,
|
33
|
+
:polygons, :pointer # array of GeoPolygon structs
|
34
|
+
end
|
35
|
+
|
36
|
+
class LinkedGeoCoord < FFI::Struct
|
37
|
+
layout :vertex, GeoCoord,
|
38
|
+
:next, LinkedGeoCoord.ptr
|
39
|
+
end
|
40
|
+
|
41
|
+
class LinkedGeoLoop < FFI::Struct
|
42
|
+
layout :first, LinkedGeoCoord.ptr,
|
43
|
+
:last, LinkedGeoCoord.ptr,
|
44
|
+
:next, LinkedGeoLoop.ptr
|
45
|
+
end
|
46
|
+
|
47
|
+
class LinkedGeoPolygon < FFI::Struct
|
48
|
+
layout :first, LinkedGeoLoop.ptr,
|
49
|
+
:last, LinkedGeoLoop.ptr,
|
50
|
+
:next, LinkedGeoPolygon.ptr
|
51
|
+
end
|
52
|
+
|
53
|
+
class CoordIJ < FFI::Struct
|
54
|
+
layout :i, :int,
|
55
|
+
:j, :int
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/h3/geo_json.rb
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
module H3
|
2
|
+
# GeoJSON helper methods.
|
3
|
+
#
|
4
|
+
# This module allows conversions between GeoJSON polygon data and a nested set of coordinates.
|
5
|
+
#
|
6
|
+
# It should be noted that H3 describes coordinates as number pairs in the form
|
7
|
+
#
|
8
|
+
# [latitude, longitude]
|
9
|
+
#
|
10
|
+
# whereas the GeoJSON standard uses
|
11
|
+
#
|
12
|
+
# [longitude, latitude]
|
13
|
+
#
|
14
|
+
# Both use degrees.
|
15
|
+
#
|
16
|
+
# == Coordinates Array
|
17
|
+
#
|
18
|
+
# We use a nested array to hold coordinates describing a geographical region.
|
19
|
+
#
|
20
|
+
# The first element in the array is an external geofence boundary, composed of an array of
|
21
|
+
# coordinates as 2-element arrays of the form [latitude, longitude].
|
22
|
+
#
|
23
|
+
# Any further elements in the array are further geofence arrays of coordinates which describe
|
24
|
+
# holes that may be present in the polygon.
|
25
|
+
#
|
26
|
+
# Specific examples are shown in the individual method details.
|
27
|
+
#
|
28
|
+
# @see http://geojson.io geojson.io - A graphical tool to see GeoJSON data rendered on a world map.
|
29
|
+
# @see https://tools.ietf.org/html/rfc7946 The GeoJSON RFC standard.
|
30
|
+
module GeoJSON
|
31
|
+
# Convert a GeoJSON document to a nested array of coordinates.
|
32
|
+
#
|
33
|
+
# @param [String] input The GeoJSON document. This can be a feature collection, feature,
|
34
|
+
# or polygon. If a feature collection is provided, the first feature is used.
|
35
|
+
#
|
36
|
+
# @example Convert a GeoJSON document of Banbury to a set of nested coordinates.
|
37
|
+
# document = "{\"type\":\"Polygon\",\"coordinates\":[
|
38
|
+
# [
|
39
|
+
# [-1.7358398437499998,52.24630137198303], [-1.8923950195312498,52.05249047600099],
|
40
|
+
# [-1.56829833984375,51.891749018068246], [-1.27716064453125,51.91208502557545],
|
41
|
+
# [-1.19476318359375,52.032218104145294], [-1.24420166015625,52.19413974159753],
|
42
|
+
# [-1.5902709960937498,52.24125614966341], [-1.7358398437499998,52.24630137198303]
|
43
|
+
# ],
|
44
|
+
# [
|
45
|
+
# [-1.58203125,52.12590076522272], [-1.476287841796875,52.12590076522272],
|
46
|
+
# [-1.46392822265625,52.075285904832334], [-1.58203125,52.06937709602395],
|
47
|
+
# [-1.58203125,52.12590076522272]
|
48
|
+
# ],
|
49
|
+
# [
|
50
|
+
# [-1.4556884765625,52.01531743663362], [-1.483154296875,51.97642166216334],
|
51
|
+
# [-1.3677978515625,51.96626938051444], [-1.3568115234375,52.0102459910103],
|
52
|
+
# [-1.4556884765625,52.01531743663362]
|
53
|
+
# ]
|
54
|
+
# ]}"
|
55
|
+
# H3.geo_json_to_coordinates(document)
|
56
|
+
# [
|
57
|
+
# [
|
58
|
+
# [52.24630137198303, -1.7358398437499998], [52.05249047600099, -1.8923950195312498],
|
59
|
+
# [51.891749018068246, -1.56829833984375], [51.91208502557545, -1.27716064453125],
|
60
|
+
# [52.032218104145294, -1.19476318359375], [52.19413974159753, -1.24420166015625],
|
61
|
+
# [52.24125614966341, -1.5902709960937498], [52.24630137198303, -1.7358398437499998]
|
62
|
+
# ],
|
63
|
+
# [
|
64
|
+
# [52.12590076522272, -1.58203125], [52.12590076522272, -1.476287841796875],
|
65
|
+
# [52.075285904832334, -1.46392822265625], [52.06937709602395, -1.58203125],
|
66
|
+
# [52.12590076522272, -1.58203125]
|
67
|
+
# ],
|
68
|
+
# [
|
69
|
+
# [52.01531743663362, -1.4556884765625], [51.97642166216334, -1.483154296875],
|
70
|
+
# [51.96626938051444, -1.3677978515625], [52.0102459910103, -1.3568115234375],
|
71
|
+
# [52.01531743663362, -1.4556884765625]
|
72
|
+
# ]
|
73
|
+
# ]
|
74
|
+
#
|
75
|
+
# @raise [ArgumentError] Failed to parse the GeoJSON document.
|
76
|
+
#
|
77
|
+
# @return [Array<Array<Array>>] Nested array of coordinates.
|
78
|
+
def geo_json_to_coordinates(input)
|
79
|
+
geom = RGeo::GeoJSON.decode(input)
|
80
|
+
coordinates = if geom.respond_to?(:first) # feature collection
|
81
|
+
geom.first.geometry.coordinates
|
82
|
+
elsif geom.respond_to?(:geometry) # feature
|
83
|
+
geom.geometry.coordinates
|
84
|
+
elsif geom.respond_to?(:coordinates) # polygon
|
85
|
+
geom.coordinates
|
86
|
+
else
|
87
|
+
failed_to_parse!
|
88
|
+
end
|
89
|
+
swap_lat_lon(coordinates) || failed_to_parse!
|
90
|
+
rescue JSON::ParserError
|
91
|
+
failed_to_parse!
|
92
|
+
end
|
93
|
+
|
94
|
+
# Convert a nested array of coordinates to a GeoJSON document
|
95
|
+
#
|
96
|
+
# @param [Array<Array<Array>>] coordinates Nested array of coordinates.
|
97
|
+
#
|
98
|
+
# @example Convert a set of nested coordinates of Banbury to a GeoJSON document.
|
99
|
+
# coordinates = [
|
100
|
+
# [
|
101
|
+
# [52.24630137198303, -1.7358398437499998], [52.05249047600099, -1.8923950195312498],
|
102
|
+
# [51.891749018068246, -1.56829833984375], [51.91208502557545, -1.27716064453125],
|
103
|
+
# [52.032218104145294, -1.19476318359375], [52.19413974159753, -1.24420166015625],
|
104
|
+
# [52.24125614966341, -1.5902709960937498], [52.24630137198303, -1.7358398437499998]
|
105
|
+
# ],
|
106
|
+
# [
|
107
|
+
# [52.12590076522272, -1.58203125], [52.12590076522272, -1.476287841796875],
|
108
|
+
# [52.075285904832334, -1.46392822265625], [52.06937709602395, -1.58203125],
|
109
|
+
# [52.12590076522272, -1.58203125]
|
110
|
+
# ],
|
111
|
+
# [
|
112
|
+
# [52.01531743663362, -1.4556884765625], [51.97642166216334, -1.483154296875],
|
113
|
+
# [51.96626938051444, -1.3677978515625], [52.0102459910103, -1.3568115234375],
|
114
|
+
# [52.01531743663362, -1.4556884765625]
|
115
|
+
# ]
|
116
|
+
# ]
|
117
|
+
# H3.coordinates_to_geo_json(coordinates)
|
118
|
+
# "{\"type\":\"Polygon\",\"coordinates\":[
|
119
|
+
# [
|
120
|
+
# [-1.7358398437499998,52.24630137198303], [-1.8923950195312498,52.05249047600099],
|
121
|
+
# [-1.56829833984375,51.891749018068246], [-1.27716064453125,51.91208502557545],
|
122
|
+
# [-1.19476318359375,52.032218104145294], [-1.24420166015625,52.19413974159753],
|
123
|
+
# [-1.5902709960937498,52.24125614966341], [-1.7358398437499998,52.24630137198303]
|
124
|
+
# ],
|
125
|
+
# [
|
126
|
+
# [-1.58203125,52.12590076522272], [-1.476287841796875,52.12590076522272],
|
127
|
+
# [-1.46392822265625,52.075285904832334], [-1.58203125,52.06937709602395],
|
128
|
+
# [-1.58203125,52.12590076522272]
|
129
|
+
# ],
|
130
|
+
# [
|
131
|
+
# [-1.4556884765625,52.01531743663362], [-1.483154296875,51.97642166216334],
|
132
|
+
# [-1.3677978515625,51.96626938051444], [-1.3568115234375,52.0102459910103],
|
133
|
+
# [-1.4556884765625,52.01531743663362]
|
134
|
+
# ]
|
135
|
+
# ]}"
|
136
|
+
#
|
137
|
+
# @raise [ArgumentError] Failed to parse the given coordinates.
|
138
|
+
#
|
139
|
+
# @return [String] GeoJSON document.
|
140
|
+
def coordinates_to_geo_json(coordinates)
|
141
|
+
coordinates = swap_lat_lon(coordinates)
|
142
|
+
outer_coords, *inner_coords = coordinates
|
143
|
+
factory = RGeo::Cartesian.simple_factory
|
144
|
+
exterior = factory.linear_ring(outer_coords.map { |lon, lat| factory.point(lon, lat) })
|
145
|
+
interior_rings = inner_coords.map do |polygon|
|
146
|
+
factory.linear_ring(polygon.map { |lon, lat| factory.point(lon, lat) })
|
147
|
+
end
|
148
|
+
polygon = factory.polygon(exterior, interior_rings)
|
149
|
+
RGeo::GeoJSON.encode(polygon).to_json
|
150
|
+
rescue RGeo::Error::InvalidGeometry, NoMethodError
|
151
|
+
invalid_coordinates!
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
# geo-json coordinates use [lon, lat], h3 uses [lat, lon]
|
157
|
+
def swap_lat_lon(coordinates)
|
158
|
+
coordinates.map { |polygon| polygon.map { |x, y| [y, x] } }
|
159
|
+
end
|
160
|
+
|
161
|
+
def failed_to_parse!
|
162
|
+
raise ArgumentError, "Could not parse given input. Please use a GeoJSON polygon."
|
163
|
+
end
|
164
|
+
|
165
|
+
def invalid_coordinates!
|
166
|
+
raise ArgumentError, "Could not parse given coordinates."
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
data/lib/h3/hierarchy.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
module H3
|
2
|
+
# Hierarchical grid functions.
|
3
|
+
#
|
4
|
+
# @see https://uber.github.io/h3/#/documentation/api-reference/hierarchy
|
5
|
+
module Hierarchy
|
6
|
+
extend H3::Bindings::Base
|
7
|
+
|
8
|
+
# @!method h3_to_parent(h3_index, parent_resolution)
|
9
|
+
#
|
10
|
+
# Derive the parent hexagon which contains the hexagon at the given H3 index.
|
11
|
+
#
|
12
|
+
# @param [Integer] h3_index A valid H3 index.
|
13
|
+
# @param [Integer] parent_resoluton The desired resolution of the parent hexagon.
|
14
|
+
#
|
15
|
+
# @example Find the parent hexagon for a H3 index.
|
16
|
+
# H3.h3_to_parent(613196570357137407, 6)
|
17
|
+
# 604189371209351167
|
18
|
+
#
|
19
|
+
# @return [Integer] H3 index of parent hexagon.
|
20
|
+
attach_function :h3_to_parent, :h3ToParent, [ :h3_index, :int ], :h3_index
|
21
|
+
|
22
|
+
# @!method max_h3_to_children_size(h3_index, child_resolution)
|
23
|
+
#
|
24
|
+
# Derive maximum number of child hexagons possible at given resolution.
|
25
|
+
#
|
26
|
+
# @param [Integer] h3_index A valid H3 index.
|
27
|
+
# @param [Integer] child_resoluton The desired resolution of the child hexagons.
|
28
|
+
#
|
29
|
+
# @example Derive maximum number of child hexagons.
|
30
|
+
# H3.max_h3_to_children_size(613196570357137407, 10)
|
31
|
+
# 49
|
32
|
+
#
|
33
|
+
# @return [Integer] Maximum number of child hexagons possible at given resolution.
|
34
|
+
attach_function :max_h3_to_children_size, :maxH3ToChildrenSize, [ :h3_index, :int ], :int
|
35
|
+
|
36
|
+
# Derive child hexagons contained within the hexagon at the given H3 index.
|
37
|
+
#
|
38
|
+
# @param [Integer] h3_index A valid H3 index.
|
39
|
+
# @param [Integer] child_resolution The desired resolution of hexagons returned.
|
40
|
+
#
|
41
|
+
# @example Find the child hexagons for a H3 index.
|
42
|
+
# H3.h3_to_children(613196570357137407, 9)
|
43
|
+
# [
|
44
|
+
# 617700169982672895, 617700169982935039, 617700169983197183, 617700169983459327,
|
45
|
+
# 617700169983721471, 617700169983983615, 617700169984245759
|
46
|
+
# ]
|
47
|
+
#
|
48
|
+
# @return [Array<Integer>] H3 indexes of child hexagons.
|
49
|
+
def h3_to_children(h3_index, child_resolution)
|
50
|
+
max_children = max_h3_to_children_size(h3_index, child_resolution)
|
51
|
+
h3_children = FFI::MemoryPointer.new(H3_INDEX, max_children)
|
52
|
+
Bindings::Private.h3_to_children(h3_index, child_resolution, h3_children)
|
53
|
+
h3_children.read_array_of_ulong_long(max_children).reject(&:zero?)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Find the maximum uncompacted size of the given set of H3 indexes.
|
57
|
+
#
|
58
|
+
# @param [Array<Integer>] compacted_set An array of valid H3 indexes.
|
59
|
+
# @param [Integer] resolution The desired resolution to uncompact to.
|
60
|
+
#
|
61
|
+
# @example Find the maximum uncompacted size of the given set.
|
62
|
+
# h3_set = [
|
63
|
+
# 617700440093229055, 617700440092704767, 617700440100569087, 617700440013012991,
|
64
|
+
# 617700440013275135, 617700440092180479, 617700440091656191, 617700440092966911,
|
65
|
+
# 617700440100831231, 617700440100044799, 617700440101617663, 617700440081956863,
|
66
|
+
# 613196840447246335
|
67
|
+
# ]
|
68
|
+
# H3.max_uncompact_size(h3_set, 10)
|
69
|
+
# 133
|
70
|
+
#
|
71
|
+
# @raise [ArgumentError] Given resolution is invalid for h3_set.
|
72
|
+
#
|
73
|
+
# @return [Integer] Maximum size of uncompacted set.
|
74
|
+
def max_uncompact_size(compacted_set, resolution)
|
75
|
+
compacted_set = compacted_set.uniq
|
76
|
+
FFI::MemoryPointer.new(H3_INDEX, compacted_set.size) do |hexagons_ptr|
|
77
|
+
hexagons_ptr.write_array_of_ulong_long(compacted_set)
|
78
|
+
size = Bindings::Private.max_uncompact_size(hexagons_ptr, compacted_set.size, resolution)
|
79
|
+
raise(ArgumentError, "Couldn't estimate size. Invalid resolution?") if size < 0
|
80
|
+
return size
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Compact a set of H3 indexes as best as possible.
|
85
|
+
#
|
86
|
+
# In the case where the set cannot be compacted, the set is returned unchanged.
|
87
|
+
#
|
88
|
+
# @param [Array<Integer>] h3_set An array of valid H3 indexes.
|
89
|
+
#
|
90
|
+
# @example Compact the given set.
|
91
|
+
# h3_set = [
|
92
|
+
# 617700440073043967, 617700440072781823, 617700440073568255, 617700440093229055,
|
93
|
+
# 617700440092704767, 617700440100569087, 617700440074092543, 617700440073830399,
|
94
|
+
# 617700440074354687, 617700440073306111, 617700440013012991, 617700440013275135,
|
95
|
+
# 617700440092180479, 617700440091656191, 617700440092966911, 617700440100831231,
|
96
|
+
# 617700440100044799, 617700440101617663, 617700440081956863
|
97
|
+
# ]
|
98
|
+
# H3.compact(h3_set)
|
99
|
+
# [
|
100
|
+
# 617700440093229055, 617700440092704767, 617700440100569087, 617700440013012991,
|
101
|
+
# 617700440013275135, 617700440092180479, 617700440091656191, 617700440092966911,
|
102
|
+
# 617700440100831231, 617700440100044799, 617700440101617663, 617700440081956863,
|
103
|
+
# 613196840447246335
|
104
|
+
# ]
|
105
|
+
#
|
106
|
+
# @raise [RuntimeError] Couldn't attempt to compact given H3 indexes.
|
107
|
+
#
|
108
|
+
# @return [Array<Integer>] Compacted set of H3 indexes.
|
109
|
+
def compact(h3_set)
|
110
|
+
h3_set = h3_set.uniq
|
111
|
+
failure = false
|
112
|
+
out = FFI::MemoryPointer.new(H3_INDEX, h3_set.size)
|
113
|
+
FFI::MemoryPointer.new(H3_INDEX, h3_set.size) do |hexagons_ptr|
|
114
|
+
hexagons_ptr.write_array_of_ulong_long(h3_set)
|
115
|
+
failure = Bindings::Private.compact(hexagons_ptr, out, h3_set.size)
|
116
|
+
end
|
117
|
+
|
118
|
+
raise "Couldn't compact given indexes" if failure
|
119
|
+
out.read_array_of_ulong_long(h3_set.size).reject(&:zero?)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Uncompact a set of H3 indexes to the given resolution.
|
123
|
+
#
|
124
|
+
# @param [Array<Integer>] compacted_set An array of valid H3 indexes.
|
125
|
+
# @param [Integer] resolution The desired resolution to uncompact to.
|
126
|
+
#
|
127
|
+
# @example Compact the given set.
|
128
|
+
# h3_set = [
|
129
|
+
# 617700440093229055, 617700440092704767, 617700440100569087, 617700440013012991,
|
130
|
+
# 617700440013275135, 617700440092180479, 617700440091656191, 617700440092966911,
|
131
|
+
# 617700440100831231, 617700440100044799, 617700440101617663, 617700440081956863,
|
132
|
+
# 613196840447246335
|
133
|
+
# ]
|
134
|
+
# H3.uncompact(h3_set)
|
135
|
+
# [
|
136
|
+
# 617700440093229055, 617700440092704767, 617700440100569087, 617700440013012991,
|
137
|
+
# 617700440013275135, 617700440092180479, 617700440091656191, 617700440092966911,
|
138
|
+
# 617700440100831231, 617700440100044799, 617700440101617663, 617700440081956863,
|
139
|
+
# 617700440072781823, 617700440073043967, 617700440073306111, 617700440073568255,
|
140
|
+
# 617700440073830399, 617700440074092543, 617700440074354687
|
141
|
+
# ]
|
142
|
+
#
|
143
|
+
# @raise [RuntimeError] Couldn't attempt to umcompact H3 indexes.
|
144
|
+
#
|
145
|
+
# @return [Array<Integer>] Uncompacted set of H3 indexes.
|
146
|
+
def uncompact(compacted_set, resolution)
|
147
|
+
max_size = max_uncompact_size(compacted_set, resolution)
|
148
|
+
|
149
|
+
failure = false
|
150
|
+
out = FFI::MemoryPointer.new(H3_INDEX, max_size)
|
151
|
+
FFI::MemoryPointer.new(H3_INDEX, compacted_set.size) do |hexagons_ptr|
|
152
|
+
hexagons_ptr.write_array_of_ulong_long(compacted_set)
|
153
|
+
failure = Bindings::Private.uncompact(hexagons_ptr, compacted_set.size, out, max_size, resolution)
|
154
|
+
end
|
155
|
+
|
156
|
+
raise "Couldn't uncompact given indexes" if failure
|
157
|
+
out.read_array_of_ulong_long(max_size).reject(&:zero?)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|