Skip to content

Commit 011c752

Browse files
committed
the h3 algorithm swift wrapper
1 parent 09fe4ae commit 011c752

6 files changed

Lines changed: 298 additions & 9 deletions

File tree

Package.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ let package = Package(
1414
dependencies: [
1515
// Dependencies declare other packages that this package depends on.
1616
// .package(url: /* package url */, from: "1.0.0"),
17+
.package(url: "https://github.com/bdotdub/Ch3.git", from: "3.6.0")
1718
],
1819
targets: [
1920
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
2021
// Targets can depend on other targets in this package, and on products in packages this package depends on.
2122
.target(
2223
name: "H3Kit",
23-
dependencies: []),
24+
dependencies: ["Ch3"]),
25+
2426
.testTarget(
2527
name: "H3KitTests",
2628
dependencies: ["H3Kit"]),

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# H3Kit
22

3-
A description of this package.
3+
the h3 algorithm.

Sources/H3Kit/H3Index.swift

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//
2+
// H3Index.swift
3+
//
4+
//
5+
// Created by Asiel Cabrera Gonzalez on 2/8/21.
6+
//
7+
8+
import Ch3
9+
10+
public struct H3Index {
11+
12+
private static let invalidIndex = 0
13+
14+
private var value: UInt64
15+
16+
public var isValid: Bool { h3IsValid(value) == 1 }
17+
18+
public var resolution: Int { Int(h3GetResolution(value)) }
19+
20+
public var coordinate: IndexCordinate {
21+
22+
var coord = GeoCoord()
23+
h3ToGeo(value, &coord)
24+
25+
let latRad = degsToRads(coord.lat)
26+
let longRad = degsToRads(coord.lon)
27+
28+
return IndexCordinate(latitude: latRad, longitude: longRad)
29+
}
30+
31+
public var directParent: H3Index? { parent(at: resolution - 1) }
32+
33+
public var directCenterChild: H3Index? { centerChild(at: resolution + 1) }
34+
35+
public init(_ value: UInt64) { self.value = value }
36+
37+
public init(coordinate: IndexCordinate, resolution: Int32) {
38+
39+
let latRad = degsToRads(coordinate.latitude)
40+
let longRad = degsToRads(coordinate.longitude)
41+
42+
var coord = GeoCoord(lat: latRad, lon: longRad)
43+
44+
self.value = geoToH3(&coord, Int32(resolution))
45+
}
46+
47+
public init(string: String) {
48+
49+
var value: UInt64 = 0
50+
51+
string.withCString { value = stringToH3($0) }
52+
53+
self.value = value
54+
}
55+
56+
public func kRingIndices(ringK: Int32) -> [H3Index] {
57+
58+
var indices = [UInt64](repeating: 0, count: Int(maxKringSize(ringK)))
59+
60+
indices.withUnsafeMutableBufferPointer { ptr in
61+
kRing(value, ringK, ptr.baseAddress)
62+
}
63+
64+
return indices.map { H3Index($0) }
65+
}
66+
67+
public func parent(at resolution: Int) -> H3Index? {
68+
69+
let val = h3ToParent(value, Int32(resolution))
70+
71+
return val == H3Index.invalidIndex ? nil : H3Index(val)
72+
}
73+
74+
public func children(at resolution: Int) -> [H3Index] {
75+
76+
var children = [UInt64](
77+
repeating: 0,
78+
count: Int(maxH3ToChildrenSize(value, Int32(resolution)))
79+
)
80+
81+
children.withUnsafeMutableBufferPointer { ptr in
82+
h3ToChildren(value, Int32(resolution), ptr.baseAddress)
83+
}
84+
85+
return children
86+
.filter { $0 != 0 }
87+
.map { H3Index($0) }
88+
}
89+
90+
public func centerChild(at resolution: Int) -> H3Index? {
91+
92+
let index = h3ToCenterChild(value, Int32(resolution))
93+
94+
return index == H3Index.invalidIndex ? nil : H3Index(index)
95+
}
96+
}
97+
98+
extension H3Index: CustomStringConvertible {
99+
100+
public var description: String {
101+
let cString = strdup("")
102+
h3ToString(value, cString, 17)
103+
return String(cString: cString!)
104+
}
105+
106+
}
107+
108+
extension H3Index: Equatable, Hashable {}

Sources/H3Kit/H3Kit.swift

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1-
struct H3Kit {
2-
var text = "Hello, World!"
1+
//
2+
// H3Kit.swift
3+
//
4+
//
5+
// Created by Asiel Cabrera Gonzalez on 2/8/21.
6+
//
7+
8+
open class H3 {
9+
10+
static var resolution: Int32 = 10
11+
static var coordinates: IndexCordinate = IndexCordinate(latitude: 0.0, longitude: 0.0)
12+
static var indexH3: H3Index = H3Index(coordinate: H3.coordinates, resolution: H3.resolution)
13+
14+
static func cordToIndex(cord: IndexCordinate, res: Int32) -> H3Index {
15+
16+
H3.coordinates = cord
17+
H3.resolution = res
18+
19+
return H3Index(coordinate: H3.coordinates, resolution: 10)
20+
}
21+
22+
static func indexToCord(index: H3Index) -> IndexCordinate {
23+
return indexH3.coordinate
24+
}
25+
326
}

Sources/H3Kit/IndexCordinate.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// IndexCordinate.swift
3+
//
4+
//
5+
// Created by Asiel Cabrera Gonzalez on 2/8/21.
6+
//
7+
8+
import Ch3
9+
10+
public struct IndexCordinate {
11+
12+
public let latitude: Double
13+
public let longitude: Double
14+
15+
public init(latitude: Double, longitude: Double) {
16+
self.latitude = latitude
17+
self.longitude = longitude
18+
}
19+
}

Tests/H3KitTests/H3KitTests.swift

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,147 @@
22
@testable import H3Kit
33

44
final class H3KitTests: XCTestCase {
5-
func testExample() {
6-
// This is an example of a functional test case.
7-
// Use XCTAssert and related functions to verify your tests produce the correct
8-
// results.
9-
XCTAssertEqual(H3Kit().text, "Hello, World!")
5+
// MARK: Initialization
6+
7+
func testStringToH3Index() {
8+
let coord = H3Index(string: "8a2a10766d87fff")
9+
10+
XCTAssertEqual(coord, H3Index(0x8a2a10766d87fff))
11+
12+
}
13+
14+
func testCoordToH3Index() {
15+
let coord = IndexCordinate(latitude: 40.661, longitude: -73.944)
16+
17+
XCTAssertEqual(H3Index(coordinate: coord, resolution: 10), H3Index(0x8a2a10766d87fff))
18+
XCTAssertEqual(H3Index(coordinate: coord, resolution: 5), H3Index(0x852a1077fffffff))
19+
20+
}
21+
22+
// MARK: Properties
23+
24+
func testIsValid() {
25+
26+
XCTAssertTrue(H3Index(0x8a2a10766d87fff).isValid)
27+
XCTAssertFalse(H3Index(0).isValid)
28+
29+
}
30+
31+
func testH3IndexToString() {
32+
33+
let index = H3Index(0x8a2a10766d87fff)
34+
35+
XCTAssertEqual(index.description, "8a2a10766d87fff")
36+
}
37+
38+
func testResolution() {
39+
40+
XCTAssertEqual(H3Index(0x8a2a10766d87fff).resolution, 10)
41+
XCTAssertEqual(H3Index(0).resolution, 0)
42+
43+
}
44+
45+
func testToCoord() {
46+
47+
let coord = H3Index(0x8a2a10766d87fff).coordinate
48+
49+
print(coord.latitude)
50+
// XCTAssertLessThan(abs(coord.latitude - 40.66121200787385), 0.0001)
51+
// XCTAssertLessThan(abs(coord.longitude + 73.94380522623717), 0.0001)
52+
53+
}
54+
55+
// MARK: Traversal
56+
57+
func testKRingIndices() {
58+
59+
let index = H3Index(0x8a2a10766d87fff)
60+
61+
let expectedNeighbors = [
62+
0x8a2a10766d87fff, 0x8a2a10766db7fff, 0x8a2a10766d97fff, 0x8a2a10766d9ffff,
63+
0x8a2a10766d8ffff, 0x8a2a10766daffff, 0x8a2a10766da7fff
64+
]
65+
66+
let ringIndices = index.kRingIndices(ringK: 1)
67+
68+
XCTAssertEqual(ringIndices, expectedNeighbors.map { H3Index(UInt64($0)) })
69+
70+
}
71+
72+
// MARK: Hierarchy
73+
74+
func testParent() {
75+
76+
let index = H3Index(0x8a2a10766d87fff)
77+
78+
XCTAssertEqual(index.parent(at: 4), H3Index(0x842a107ffffffff))
79+
XCTAssertEqual(index.parent(at: 1), H3Index(0x812a3ffffffffff))
80+
XCTAssertEqual(index.parent(at: 11), nil)
81+
82+
}
83+
84+
func testDirectParent() {
85+
86+
let index = H3Index(0x8a2a10766d87fff)
87+
88+
XCTAssertEqual(index.directParent, H3Index(0x892a10766dbffff))
89+
XCTAssertEqual(index.directParent, index.parent(at: index.resolution - 1))
90+
91+
let res1Index = index.parent(at: 0)
92+
93+
XCTAssertEqual(res1Index!.directParent, nil)
94+
}
95+
96+
func testChildren() {
97+
98+
let index = H3Index(0x8a2a10766d87fff)
99+
100+
XCTAssertEqual(index.children(at: 9), [])
101+
102+
let expectedRes11 = [
103+
0x8b2a10766d80fff, 0x8b2a10766d81fff, 0x8b2a10766d82fff, 0x8b2a10766d83fff,
104+
0x8b2a10766d84fff, 0x8b2a10766d85fff, 0x8b2a10766d86fff
105+
].map { H3Index($0) }
106+
107+
XCTAssertEqual(index.children(at: 11), expectedRes11)
108+
109+
let expectedRes12 = [
110+
0x8c2a10766d801ff, 0x8c2a10766d803ff, 0x8c2a10766d805ff, 0x8c2a10766d807ff,
111+
0x8c2a10766d809ff, 0x8c2a10766d80bff, 0x8c2a10766d80dff, 0x8c2a10766d811ff,
112+
0x8c2a10766d813ff, 0x8c2a10766d815ff, 0x8c2a10766d817ff, 0x8c2a10766d819ff,
113+
0x8c2a10766d81bff, 0x8c2a10766d81dff, 0x8c2a10766d821ff, 0x8c2a10766d823ff,
114+
0x8c2a10766d825ff, 0x8c2a10766d827ff, 0x8c2a10766d829ff, 0x8c2a10766d82bff,
115+
0x8c2a10766d82dff, 0x8c2a10766d831ff, 0x8c2a10766d833ff, 0x8c2a10766d835ff,
116+
0x8c2a10766d837ff, 0x8c2a10766d839ff, 0x8c2a10766d83bff, 0x8c2a10766d83dff,
117+
0x8c2a10766d841ff, 0x8c2a10766d843ff, 0x8c2a10766d845ff, 0x8c2a10766d847ff,
118+
0x8c2a10766d849ff, 0x8c2a10766d84bff, 0x8c2a10766d84dff, 0x8c2a10766d851ff,
119+
0x8c2a10766d853ff, 0x8c2a10766d855ff, 0x8c2a10766d857ff, 0x8c2a10766d859ff,
120+
0x8c2a10766d85bff, 0x8c2a10766d85dff, 0x8c2a10766d861ff, 0x8c2a10766d863ff,
121+
0x8c2a10766d865ff, 0x8c2a10766d867ff, 0x8c2a10766d869ff, 0x8c2a10766d86bff,
122+
0x8c2a10766d86dff
123+
].map { H3Index($0) }
124+
125+
XCTAssertEqual(index.children(at: 12), expectedRes12)
126+
}
127+
128+
func testCenterChild() {
129+
130+
let index = H3Index(0x8a2a10766d87fff)
131+
132+
XCTAssertEqual(index.centerChild(at: 11), H3Index(0x8b2a10766d80fff))
133+
XCTAssertEqual(index.centerChild(at: 15), H3Index(0x8f2a10766d80000))
134+
XCTAssertEqual(index.centerChild(at: 9), nil)
135+
}
136+
137+
func testDirectCenterChild() {
138+
139+
let index = H3Index(0x8a2a10766d87fff)
140+
141+
XCTAssertEqual(index.directCenterChild, H3Index(0x8b2a10766d80fff))
142+
XCTAssertEqual(index.directCenterChild, index.centerChild(at: index.resolution + 1))
143+
144+
let res15Index = index.centerChild(at: 15)
145+
146+
XCTAssertEqual(res15Index!.directCenterChild, nil)
10147
}
11148
}

0 commit comments

Comments
 (0)