Skip to content

dfellis/h3-node

Repository files navigation

H3-Node

Unit Testing License

H3 Bindings to Node using N-API

Install

To install this package, you need several development tools available: git, gyp, make, cmake, and a C compiler (gcc or clang). Once those are available, simply

yarn add h3-node

-- or --

npm i h3-node

Versions

The 1.X.Y versions released before have varying stages of completeness of the API. From version 3.2.0 and on the major and minor versions will match the upstream C Implementation's major and minor versions, while the patch version will increment independently, including fixes for the bindings themselves as well as fixes from upstream.

Usage

h3-node is a drop-in replacement for H3-js. You can use the H3-js API reference for the available methods and the official H3 documentation to fully understand the purposes of the methods. Below is an abbreviated usage of every available method.

const h3 = require('h3-node')

const h3index = h3.latLngToCell(37.77, -122.43, 9)
const h3center = h3.cellToLatLng(h3index)
const h3outline = h3.cellToBoundary(h3index)
const h3area = h3.cellArea(h3index, h3.UNITS.m2)

const res = h3.getResolution(h3index)
const baseCell = h3.getBaseCellNumber(h3index)
const valid = h3.isValidCell(h3index)
const resIsClassIII = h3.isResClassIII(h3index)
const isPentagon = h3.isPentagon(h3index)
const faces = h3.getIcosahedronFaces(h3Index)
const neighbors = h3.gridDisk(h3index, 1)
const threeRings = h3.gridDiskDistances(h3index, 3)
const ring2Away = h3.gridRingUnsafe(h3index, 2)
const index2 = h3.latLngToCell(38.88, -122.34, 9)
const hexLine = h3.gridPathCells(h3index, index2)
const hexDistance = h3.gridDistance(h3index, index2)
const coords = h3.cellToLocalIj(h3index, index2)
const index2again = h3.localIjToCell(h3index, coords)
const parent = h3.cellToParent(h3index, 8)
const immediateChildren = h3.cellToChildren(h3index, 10)
const centerChild = h3.cellToCenterChild(h3index, 10)
const packedRings = h3.compactCells([].concat(...threeRings))
const unpackedRings = h3.uncompactCells(packedRings, 9)
const bayAreaHexes = h3.polygonToCells([
    [37.77, -122.43],
    [37.55, -122.43],
    [37.55, -122.23],
    [37.77, -122.23],
    [37.77, -122.43],
  ], 9)
const bayAreaHexGeo = h3.cellsToMultiPolygon(bayAreaHexes)
const areNeighbors = h3.areNeighborCells(neighbors[0], neighbors[1])
const edgeIndex = h3.cellsToDirectedEdge(neighbors[0], neighbors[1])
const isValidEdge = h3.isValidDirectedEdge(edgeIndex)
const edgeLenM = h3.edgeLength(edgeIndex, h3.UNITS.m)
const origin = h3.getDirectedEdgeOrigin(edgeIndex)
const destination = h3.getDirectedEdgeDestination(edgeIndex)
const [start, end] = h3.directedEdgeToCells(edgeIndex)
const edges = h3.originToDirectedEdges(h3index)
const edgeBoundaries = edges.map(h3.directedEdgeToBoundary)
const sfCoordsRads = [37.77, -122.43].map(h3.degsToRads)
const sfCoordsDegs = sfCoordsRads.map(h3.radsToDegs)
const resStats = Array.from(new Array(16), (x, res) => ({
  res,
  numHexagons: h3.numHexagons(res),
  edgeLength: {
    m: h3.getHexagonEdgeLengthAvg(res, h3.UNITS.m),
    km: h3.getHexagonEdgeLengthAvg(res, h3.UNITS.km),
  },
  hexArea: {
    m2: h3.getHexagonAreaAvg(res, h3.UNITS.m2),
    km2: h3.getHexagonAreaAvg(res, h3.UNITS.km2),
  },
  pentagons: h3.getPentagons(res),
}))
const res0Indexes = h3.getRes0Cells()

Why another H3 for Node?

h3-js is an emscripten transpilation of the H3 code into Javascript with a Javascript wrapper file similar to a binding in spirit to handle calling these quasi-C functions for you.

Being 100% javascript it works across the entire Javascript ecosystem (especially when paired with browserify or babel to handle the Node-isms for non-Node JS environments), but that portability comes at a cost in performance. Preliminary benchmarks show a significant speedup with the N-API approach:

damocles@elack:~/oss/h3-node(upgrade-to-v4)$ yarn test
yarn run v1.22.19
$ nodeunit test

index
✔ isValidCell_array
✔ isValidCell_uint32array
✔ cellToLatLng_array
✔ cellToLatLng_uint32array
✔ latLngToCell
✔ cellToLatLng
✔ cellToBoundary
✔ getResolution
✔ getBaseCellNumber
✔ isValidCell
✔ isResClassIII
✔ isPentagon
✔ getIcosahedronFaces
✔ gridDisk
✔ gridDiskDistances
undefined
undefined
✔ gridRingUnsafe
✔ gridDistance
✔ cellToLocalIj
undefined
undefined
✔ localIjToCell
✔ cellToParent
✔ cellToChildren
✔ cellToCenterChild
✔ compactCells
✔ uncompactCells
✔ polygonToCells
✔ polygonToCellsWithHoles
✔ cellsToMultiPolygon
✔ cellsToMultiPolygonGeoJsonMode
✔ cellsToMultiPolygonTrueMultiPolygon
✔ areNeighborCells
✔ cellsToDirectedEdge
✔ isValidDirectedEdge
✔ getDirectedEdgeOrigin
✔ getDirectedEdgeDestination
✔ directedEdgeToCells
✔ originToDirectedEdges
✔ directedEdgeToBoundary
✔ degsToRads
✔ radsToDegs
✔ getNumCells
✔ getHexagonEdgeLengthAvg
✔ edgeLength
✔ getHexagonAreaAvg
✔ cellArea
✔ greatCircleDistance
✔ getRes0Cells
✔ getPentagons

latLngToCell Benchmark:
H3-js time in ns:    8407061
H3-node time in ns:  1696666
✔ latLngToCellBenchmark

cellToLatLng Benchmark:
H3-js time in ns:    3957443
H3-node time in ns:  1500690
✔ cellToLatLngBenchmark

cellToBoundary Benchmark:
H3-js time in ns:    7288756
H3-node time in ns:  5075260
✔ cellToBoundaryBenchmark

getResolution Benchmark:
H3-js time in ns:    2497192
H3-node time in ns:  299202
✔ getResolutionBenchmark

getBaseCellNumber Benchmark:
H3-js time in ns:    341876
H3-node time in ns:  304160
✔ getBaseCellNumberBenchmark

isValidCell Benchmark:
H3-js time in ns:    259113
H3-node time in ns:  263024
✔ isValidCellBenchmark

isResClassIII Benchmark:
H3-js time in ns:    649109
H3-node time in ns:  470734
✔ isResClassIIIBenchmark

isPentagon Benchmark:
H3-js time in ns:    477089
H3-node time in ns:  310935
✔ isPentagonBenchmark

getIcosahedronFaces Benchmark:
H3-js time in ns:    4100479
H3-node time in ns:  1311698
✔ getIcosahedronFacesBenchmark

gridDisk Benchmark:
H3-js time in ns:    119649189
H3-node time in ns:  41318171
✔ gridDiskBenchmark

gridDiskDistances Benchmark:
H3-js time in ns:    104035995
H3-node time in ns:  53421119
✔ gridDiskDistancesBenchmark

gridRingUnsafe Benchmark:
H3-js time in ns:    13028001
H3-node time in ns:  9361658
✔ gridRingUnsafeBenchmark

gridDistance Benchmark:
H3-js time in ns:    6042499
H3-node time in ns:  1839702
✔ gridDistanceBenchmark

cellToLocalIj Benchmark:
H3-js time in ns:    3213419
H3-node time in ns:  2277539
✔ cellToLocalIjBenchmark

localIjToCell Benchmark:
H3-js time in ns:    10561051
H3-node time in ns:  1960318
✔ localIjToCellBenchmark

gridPathCells Benchmark:
H3-js time in ns:    66085174
H3-node time in ns:  48151768
✔ gridPathCellsBenchmark

cellToParent Benchmark:
H3-js time in ns:    1062642
H3-node time in ns:  411856
✔ cellToParentBenchmark

cellToChildren Benchmark:
H3-js time in ns:    985474186
H3-node time in ns:  895306179
✔ cellToChildrenBenchmark

cellToCenterChild Benchmark:
H3-js time in ns:    1203932
H3-node time in ns:  454949
✔ cellToCenterChildBenchmark

compactCells Benchmark:
H3-js time in ns:    120589679
H3-node time in ns:  49026745
✔ compactCellsBenchmark

uncompactCells Benchmark:
H3-js time in ns:    51719983
H3-node time in ns:  43166044
✔ uncompactCellsBenchmark

polygonToCells Benchmark:
H3-js time in ns:    51227180
H3-node time in ns:  27323211
✔ polygonToCellsBenchmark

polygonToCellsWithHoles Benchmark:
H3-js time in ns:    57260529
H3-node time in ns:  41699019
✔ polygonToCellsWithHolesBenchmark

cellsToMultiPolygon Benchmark:
H3-js time in ns:    471233062
H3-node time in ns:  393672608
✔ cellsToMultiPolygonBenchmark

cellsToMultiPolygonGeoJsonMode Benchmark:
H3-js time in ns:    443336590
H3-node time in ns:  390950036
✔ cellsToMultiPolygonGeoJsonModeBenchmark

cellsToMultiPolygonTrueMultiPolygon Benchmark:
H3-js time in ns:    578690116
H3-node time in ns:  518527732
✔ cellsToMultiPolygonTrueMultiPolygonBenchmark

areNeighborCells Benchmark:
H3-js time in ns:    2038122
H3-node time in ns:  647922
✔ areNeighborCellsBenchmark

cellsToDirectedEdge Benchmark:
H3-js time in ns:    1948026
H3-node time in ns:  993080
✔ cellsToDirectedEdgeBenchmark

isValidDirectedEdge Benchmark:
H3-js time in ns:    635001
H3-node time in ns:  294313
✔ isValidDirectedEdgeBenchmark

getDirectedEdgeOrigin Benchmark:
H3-js time in ns:    717554
H3-node time in ns:  400194
✔ getDirectedEdgeOriginBenchmark

getDirectedEdgeDestination Benchmark:
H3-js time in ns:    1119284
H3-node time in ns:  978832
✔ getDirectedEdgeDestinationBenchmark

directedEdgeToCells Benchmark:
H3-js time in ns:    1186332
H3-node time in ns:  933715
✔ directedEdgeToCellsBenchmark

originToDirectedEdges Benchmark:
H3-js time in ns:    2235844
H3-node time in ns:  2371965
✔ originToDirectedEdgesBenchmark

directedEdgeToBoundary Benchmark:
H3-js time in ns:    3099228
H3-node time in ns:  2619554
✔ directedEdgeToBoundaryBenchmark

degsToRads Benchmark:
H3-js time in ns:    33804
H3-node time in ns:  98267
✔ degsToRadsBenchmark

radsToDegs Benchmark:
H3-js time in ns:    87302
H3-node time in ns:  90584
✔ radsToDegsBenchmark

getNumCells Benchmark:
H3-js time in ns:    330701
H3-node time in ns:  88140
✔ getNumCellsBenchmark

getHexagonEdgeLengthAvg Benchmark:
H3-js time in ns:    352072
H3-node time in ns:  111467
✔ getHexagonEdgeLengthAvgBenchmark

edgeLength Benchmark:
H3-js time in ns:    4884662
H3-node time in ns:  2593644
✔ edgeLengthBenchmark

getHexagonAreaAvg Benchmark:
H3-js time in ns:    320085
H3-node time in ns:  112515
✔ getHexagonAreaAvgBenchmark

cellArea Benchmark:
H3-js time in ns:    8145085
H3-node time in ns:  3914910
✔ cellAreaBenchmark

greatCircleDistance Benchmark:
H3-js time in ns:    1210916
H3-node time in ns:  730614
✔ greatCircleDistanceBenchmark

getRes0Cells Benchmark:
H3-js time in ns:    35733206
H3-node time in ns:  28694344
✔ getRes0CellsBenchmark

getPentagons Benchmark:
H3-js time in ns:    5991654
H3-node time in ns:  3668299
✔ getPentagonsBenchmark

OK: 452 assertions (9021ms)
Done in 9.18s.

h3-node is a Node N-API binding of the original C H3 code to provide a higher-performance option in backend Node.js applications.

That makes h3-node a "nice to have" but not as required as h3-js. (In a similar vein, I intend to write an h3-wasm for higher-performance H3 in modern browsers that can be a drop-in replacement for h3-js when WebAssembly is present.)